1、创建模型 1.1 使用save方法创建模型 调用Eloquent模型类的save方法即可创建模型并插入数据到数据库:
1 2 3 4 5 6 7 8 9 10 |
$post = new Post; $post->title = 'test 4'; $post->content = 'test content'; $post->user_id = 1; $post->cat_id = 1; if($post->save()){ echo '添加文章成功!'; }else{ echo '添加文章失败!'; } |
在浏览器中访问http://laravel.app:8000/test,如果输出内容为:
1 |
添加文章成功! |
则表明插入成功,我们去数据库查看数据,确实新增了一条记录: 1.2 使用create方法插入数据 除此之外还可以使用create方法插入数据,由于该方法中用到了批量赋值(Mass Assignment),所以我们需要在模型类中设置$fillable属性或者$guarded属性,以表明哪些属性可以通过该方法设置,哪些不可以。 开始之前,我们先解释下什么是批量赋值,以及为什么要使用批量赋值。 批量赋值的英文名称是Mass Assignment,所谓的批量赋值是指当我们将一个数组发送到模型类用于创建新的模型实例的时候(通常是表单请求数据),我们可以简单通过如下方式实现:
1 |
$post = Post::create(Input::all()); |
而不是像使用save方法那样一个一个的设置属性值,如果模型属性很多的话,使用save简直是噩梦有木有。 但事物总是相对的,使用批量赋值是很方便,但同时也带来了安全隐患,很多时候模型类的某些属性值不是我们所期望通过批量赋值修改的,比如用户模型有个user_type属性,如果用户通过请求数据将其类型修改为管理员类型,这显然是不允许的,正是基于这一考虑,Eloquent模型类为我们提供了$fillable属性和$guarded属性,我们可以将其分别看作“白名单”和“黑名单”,定义在$fillable中的属性可以通过批量赋值进行赋值,而定义在$guarded中的属性在批量赋值时会被过滤掉。 那么如果我们确实想要修改定义在$guarded中的属性怎么办?答案是使用save方法。 此外需要注意的是$fillable和$guarded方法同时只能定义一个,原因嘛很简单,非黑即白,定义了一个另外一个也就确定了。 可见批量赋值不仅为我们创建模型提供了便利,还避免了安全隐患,提高了系统的安全性。 下面我们来演示一下批量赋值的使用。首先在Post模型中定义$guarded属性如下:
1 |
protected $guarded = ['views','user_id','updated_at','created_at']; |
然后在控制器中实现创建模型实例的逻辑:
1 2 3 4 5 6 7 8 9 |
$input = [ 'title'=>'test 5', 'content'=>'test content', 'cat_id'=>1, 'views'=>100, 'user_id'=>2 ]; $post = Post::create($input); dd($post); |
在浏览器中输入http://laravel.app:8000/test,则页面输出: 可见user_id和views字段都没有插入进去,这正是$guarded发挥了作用,如果要设置这两个值也很简单:
1 2 3 4 5 6 7 8 9 10 11 12 |
$input = [ 'title'=>'test 5', 'content'=>'test content', 'cat_id'=>1, 'views'=>100, 'user_id'=>2 ]; $post = Post::create($input); $post->user_id = 2; $post->views = 100; $post->save(); dd($post); |
对应输出如下: 1.3 其他插入数据的方法 Eloquent模型类还支持其它插入数据的方法——firstOrCreate和firstOrNew,两者都是先通过通过传入属性值在数据库中查找匹配记录,如果没有找到则创建一个新的模型实例,不同之处在于后者不会将数据持久化到数据库,需要调用save方法才行。 2、更新模型 2.1 使用save方法更新模型 save方法还可以用于更新模型,要更新模型数据,先要获取该模型实例,然后修改模型属性,再调用save方法保存即可:
1 2 3 4 5 6 7 |
$post = Post::find(1); $post->title = 'test 1 title'; if($post->save()){ echo '更新文章成功!'; }else{ echo '更新文章失败!'; } |
在浏览器中访问http://laravel.app:8000/test,如果显示:
1 |
更新文章成功! |
则表明数据库对应表记录更新成功: 2.2 使用update方法更新数据 和create相对应的,Eloquent模型类还支持使用update方法更新数据,同样要用到批量赋值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$input = [ 'title'=>'test 6 title', 'content'=>'test content 6', 'cat_id'=>1, 'views'=>200, 'user_id'=>1 ]; $post = Post::find(6); if($post->update($input)){ echo '更新文章成功!'; dd($post); }else{ echo '更新文章失败!'; } |
对应输出为: 可见user_id和views并没有更新。 from:http://laravelacademy.org/post/984.html
View Details1、引子 在正式进入本节的之前,让我们先来看看什么是ORM。 ORM,即 Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在操作具体的 业务对象时,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法即可。 ORM 两种最常见的实现方式是 ActiveRecord 和 DataMapper,ActiveRecord 尤其流行,在很多框架中都能看到它的身影。两者的区别主要在于 ActiveRecord 中模型与数据表一一对应,而 DataMapper 中模型与数据表是完全分离的。 Laravel 中的 Eloquent ORM 使用的也是 ActiveRecord 实现方式,每一个 Eloquent 模型类对应着数据库中的一张表,我们通过调用模型类的相应方法实现对数据库的增删改查。 2、定义模型 2.1 创建模型 我们使用Artisan命令make:model生成模型类,模型类默认位于app目录下,我们也可以在创建时指定生成目录:
1 |
php artisan make:model Models/Post |
这样就会在app目录下生成一个Models目录,并且在Models目录下生成一个Post模型类。Laravel 中所有模型类继承自Illuminate\Database\Eloquent\Model类。 2.2 指定表名 如果不手动指定,默认Post对应的数据表为posts,以此类推。也可以通过设置$table属性自定义表名:
1 |
public $table = 'posts'; |
2.3 指定主键 Eloquent默认数据表主键为id,当然也可以通过设置$primaryKey属性来自定义主键:
1 |
public $primaryKey = 'id'; |
2.4 时间戳设置 默认情况下,Eloquent模型类会自动管理时间戳列create_at和update_at(如果定义迁移时设置了这两列的话),如果要取消自动管理,可以设置$timestamps属性为false:
1 |
public $timestamps = false; |
还有,如果你想要设置时间戳的格式,可以使用$dateFormat属性,该属性决定了日期时间以何种格式存入数据库,以及以何种格式显示:
1 2 |
//设置日期时间格式为Unix时间戳 protected $dateFormat = 'U'; |
更多关于日期时间格式设置,请参考php官方函数date中format部分。 3、查询数据 3.1 获取多个模型 我们可以使用Eloquent模型上的all方法获取所有模型实例,比如我们通过如下方法获取所有文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Models\Post; class TestController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { //获取多个Eloquent模型 $posts = Post::all(); dd($posts); } } |
对应输出结果为: 可见输出结果是模型数组集合,每一个$items元素对应一个Post模型实例。 此外,需要了解的是每一个Eloquent模型本身都是一个查询构建器,所有我们可以调用所有查询构建器上的方法,只不过第一个方法调用都要使用静态方法调用:
1 2 |
$posts = Post::where('id','<',3)->orderBy('id','desc')->take(1)->get(); dd($posts); |
对应输出结果为: 也许你已经注意到了,模型查询返回结果都是Illuminate\Database\Eloquent\Collection的一个实例,该类实现了ArrayAccess接口,所以我们可以像访问数组一样访问该实例,此外,该Collection类还提供了很多其它有用的方法对查询结果进行处理,详见源码。 既然Eloquent模型是查询构建器,自然也支持分组块获取数据:
1 2 3 4 5 |
Post::chunk(2,function($posts){ foreach ($posts as $post) { echo $post->title.'<br>'; } }); |
输出结果如下:
1 2 3 |
test 1 test 2 test 3 |
3.2 获取单个模型 可以使用查询构建器方法获取单个模型实例:
1 2 |
$post = Post::where('id',1)->first(); dd($post); |
当然也可以通过Eloquent模型类提供的快捷方法find:
1 |
$post = Post::find(1); |
两者输出结果一样: 如果没有找到对应的表记录,会输出null,如果我们想要捕获查询结果为空的异常并进行处理,比如跳转到404页面,可以使用findOrFail或者firstOrFail方法,如果表记录存在,两者返回获取到的第一条记录,否则抛出Illuminate\Database\Eloquent\ModelNotFoundException异常。 3.3 聚合函数查询 如果要对查询结果进行计数、统计、最大值/最小值、平均数等聚合运算,可以使用查询构建器上的对应方法,我们我们查询文章总数:
1 2 |
$count = Post::where('id','>',0)->count(); echo $count; |
输出结果为3,又或者我们想要获取文章最大阅读数:
1 2 |
$views = Post::where('id','>',0)->max('views'); echo $views; |
输出结果为800。 from:http://laravelacademy.org/post/966.html
View Details容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。本文就从这里开始。 IoC 容器 —— Laravel 的核心 Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。 本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转) 和 DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。 IoC 容器诞生的故事 讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。 超人和超能力,依赖的产生 面向对象编程,有以下几样东西无时不刻的接触:接口、类还有对象。这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例。当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码辅助学习。 怪物横行的世界,总归需要点超级人物来摆平。 我们把一个“超人”作为一个类,
1 |
class Superman {} |
我们可以想象,一个超人诞生的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个对象,为这个对象定义一个描述他的类吧。一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们以后再丰富:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Power { /** * 能力值 */ protected $ability; /** * 能力范围或距离 */ protected $range; public function __construct($ability, $range) { $this->ability = $ability; $this->range = $range; } } |
这时候我们回过头,修改一下之前的“超人”类,让一个“超人”创建的时候被赋予一个超能力:
1 2 3 4 5 6 7 8 9 |
class Superman { protected $power; public function __construct() { $this->power = new Power(999, 100); } } |
这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。 所谓“依赖”,就是 “我若依赖你,我就不能离开你”。 在一个贯彻面向对象编程的项目中,这样的依赖随处可见。少量的依赖并不会有太过直观的影响,我们随着这个例子逐渐铺开,让大家慢慢意识到,当依赖达到一个量级时,是怎样一番噩梦般的体验。当然,我也会自然而然的讲述如何解决问题。 一堆乱麻 —— 可怕的依赖 之前的例子中,超能力类实例化后是一个具体的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完全。我们现在进行修改,我们假设超人可以有以下多种超能力: 飞行,属性有:飞行速度、持续飞行时间 蛮力,属性有:力量值 能量弹,属性有:伤害值、射击距离、同时射击个数 我们创建了如下类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Flight { protected $speed; protected $holdtime; public function __construct($speed, $holdtime) {} } class Force { protected $force; public function __construct($force) {} } class Shot { protected $atk; protected $range; protected $limit; public function __construct($atk, $range, $limit) {} } |
为了省事儿我没有详细写出 __construct() 这个构造函数的全部,只写了需要传递的参数。 好了,这下我们的超人有点“忙”了。在超人初始化的时候,我们会根据需要来实例化其拥有的超能力吗,大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Superman { protected $power; public function __construct() { $this->power = new Fight(9, 100); // $this->power = new Force(45); // $this->power = new Shot(99, 50, 2); /* $this->power = array( new Force(45), new Shot(99, 50, 2) ); */ } } |
我们需要自己手动的在构造函数内(或者其他方法里)实例化一系列需要的类,这样并不好。可以想象,假如需求变更(不同的怪物横行地球),需要更多的有针对性的新的超能力,或者需要变更超能力的方法,我们必须 重新改造 超人。换句话说就是,改变超能力的同时,我还得重新制造个超人。效率太低了!新超人还没创造完成世界早已被毁灭。 这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时更换,只需要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。这样的话就不要整个重新来过了。 对,就是这样的。 我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。 工厂模式,依赖转移! 当然,实现控制反转的方法有几种。在这之前,不如我们先了解一些好玩的东西。 我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式: 工厂模式。 工厂模式,顾名思义,就是一个类所依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。 我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅需要通过一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class SuperModuleFactory { public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); } } } |
这时候,超人 创建之初就可以使用这个工厂!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Superman { protected $power; public function __construct() { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 $this->power = $factory->makeModule('Fight', [9, 100]); // $this->power = $factory->makeModule('Force', [45]); // $this->power = $factory->makeModule('Shot', [99, 50, 2]); /* $this->power = array( $factory->makeModule('Force', [45]), $factory->makeModule('Shot', [99, 50, 2]) ); */ } } |
可以看得出,我们不再需要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可满足需求。但这样似乎和以前区别不大,只是没有那么多 new 关键字。其实我们稍微改造一下这个类,你就明白,工厂类的真正意义和价值了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Superman { protected $power; public function __construct(array $modules) { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 foreach ($modules as $moduleName => $moduleOptions) { $this->power[] = $factory->makeModule($moduleName, $moduleOptions); } } } // 创建超人 $superman = new Superman([ 'Fight' => [9, 100], 'Shot' => [99, 50, 2] ]); |
现在修改的结果令人满意。现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修改了或者增加了新的超能力,只需要针对修改 SuperModuleFactory 即可。扩充超能力的同时不再需要重新编辑超人的类文件,使得我们变得很轻松。但是,这才刚刚开始。 […]
View Details1、连接查询(join) 连接查询指的是将两张表或多张表关联到一起进行查询,获取一个表的行与另一个表的行匹配的数据。常见的连接查询包括内连接(等值连接)、左(外)连接、右(外)连接和交叉连接(完全连接)等。下面这张图形象的展示了这几种连接查询所获取的结果集: 下面我们简单演示下内连接和左连接。我们将用户表users和文章表posts关联到一起进行查询,在此之前,我们先创建posts表,其字段及初始值如下: 其中user_id对应users表中的用户id。 1.1 内连接 内连接用于获取两张表结果集的交集部分,我们可以使用查询构建器的join方法进行内连接查询:
1 2 |
$users = DB::table('users')->join('posts','users.id','=','posts.user_id')->get(); dd($users); |
显示结果如下: 1.2 左连接 左连接的结果集指定的左表的所有行,如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值(null)。我们使用查询构建器的leftJoin方法进行左连接查询:
1 2 |
$users = DB::table('users')->leftJoin('posts','users.id','=','posts.user_id')->get(); dd($users); |
对应的输出结果为: 1.3 更加复杂的连接查询 很多时候,连接查询的查询条件往往更为复杂,并不是一个简单的join/leftJoin方法就能搞定的,那么我们如何添加更加复杂的查询条件呢?使用JoinClause代替条件参数:
1 2 3 4 5 |
$users = DB::table('users')->join('posts',function($join){ $join->on('users.id','=','posts.user_id') -><a title="View all posts in where" href="http://laravelacademy.org/tags/where" target="_blank">where</a>('posts.id','>',1); })->get(); dd($users); |
输出结果为: 2、联合查询(union) 联合查询用于将两个或更多查询的结果集组合为单个结果集,该结果集包含联合查询中所有查询的全部行。UNION的结果集列名与UNION运算符中第一个Select语句的结果集的列名相同,另一个Select语句的结果集列名将被忽略,且其他查询字段数必须和第一个相同。Laravel查询构建器中我们使用union方法进行联合查询:
1 2 3 |
$users = DB::table('users')->where('id','<',3); $users = DB::table('users')->where('id','>',2)->union($users)->get(); dd($users); |
输出结果为: 3、where子句 使用查询构建器上的where方法可以添加自定义查询条件,调用该方法需要传入三个参数:第一个列名,第二个是操作符,第三个是比较值:
1 2 |
$user = DB::table('users')->where('name','=','Laravel')->get(); dd($user); |
如果操作符为“=”,该语句也可简化为:
1 |
$user = DB::table('users')->where('name','Laravel')->get(); |
输出结果为: 需要注意的是查询构建器支持方法链,这意味着如果有多个查询条件且这个多个条件是AND连接,可以在get之前使用多个where方法。如果多个条件使用OR连接,可以使用orWhere方法:
1 |
$user = DB::table('users')->where('name','Laravel')->orWhere('name','Academy')->get(); |
对应输出为: 更多where子句查询条件可查看Illuminate\Database\Query\Builder源码。 4、排序 查询构建器使用orderBy方法对查询结果进行排序:
1 2 |
$users = DB::table('users')->orderBy('id','desc')->get(); dd($users); |
根据代码可以看到orderBy方法需要传入两个参数,第一个是排序字段,第二个是排序方向,asc代表升序,desc代表倒序,上述代码输出为: 5、分组 为了更好地演示分组,我们给数据表posts新增两个字段:cat_id和views,代表分类ID和浏览数: 分组一般用于聚合查询,接下来我们使用groupBy方法对查询结果进行分组,比如我们可以统计每个分类下有几篇文章:
1 2 |
$posts = DB::table('posts')->select('cat_id',DB::raw('COUNT(id) as num'))->groupBy('cat_id')->get(); dd($posts); |
输出如下: 我们还可以使用having方法为分组加上条件,比如我们可以统计总浏览数大于500的分类:
1 2 |
$posts = DB::table('posts')->select('cat_id',DB::raw('SUM(views) as views'))->groupBy('cat_id')->having('views','>',500)->get(); dd($posts); |
输出结果为: 注意:having中的条件字段必须出现在select查询字段中,否则会报错。 6、分页 查询构建器中使用skip和take对查询结果进行分页,相当于SQL语句中的limit语句:
1 2 |
$posts = DB::table('posts')->skip(0)->take(2)->get(); dd($posts); |
对应的输出结果为: from:http://laravelacademy.org/post/920.html
View Details获取查询构建器很简单,还是要依赖DB门面,我们使用DB门面的table方法,传入表名,即可获取该表的查询构建器:
1 |
$users = DB::table('users'); |
这样我们就获取到了$users表的查询构建器,实际上,底层返回的是Illuminate\Database\Query\Builder的实例,我们对查询构建器的所有操作都是调用该实例对应类上的方法。下面我们就列举查询构建器的一些常用方法,我们还是沿用上一节创建的$users表做演示说明 。 1、新增数据 使用查询构建器的insert方法即可插入一条/多条数据:
1 2 3 4 5 |
DB::table('users')->insert([ ['id'=>1,'name'=>'<a title="View all posts in Laravel" href="http://laravelacademy.org/tags/laravel" target="_blank">Laravel</a>','email'=>'laravel@test.com','password'=>'123'], ['id'=>2,'name'=>'Academy','email'=>'academy@test.com','password'=>'123'], ['id'=>3,'name'=>'LaravelAcademy','email'=>'laravel-academy@test.com','password'=>'123'] ]); |
执行成功后即可在数据表$users中插入三条记录。有时候,我们需要插入记录后获取自增ID,可以使用insertGetId方法:
1 2 3 |
$insertId = DB::table('users')->insertGetId( ['name'=>'Laravel-Academy','email'=>'laravelacademy@test.com','password'=>'456'] ); |
2、更新数据 更新表记录很简单,使用查询构建器的update方法即可:
1 |
$affected = DB::table('users')->where('name','Laravel-Academy')->update(['password'=>'123']); |
该方法返回受影响的函数。 3、删除数据 使用delete方法删除表记录,删除方法和更新方法类似,返回被删除的行数:
1 |
$deleted = DB::table('users')->where('id', '>', 3)->delete(); |
如果我们是要删除整个数据表数据,则略去where条件,如果是要清空数据表还要将自增ID置为0,可以使用truncate方法:
1 |
DB::table('users')->truncate(); |
4、基本查询 4.1 获取所有表记录 使用get方法即可获取一张表的所有记录:
1 2 |
$users = DB::table('users')->get(); dd($users); |
打印结果如下: 如果是获取指定列的数据,则需要加上select条件:
1 2 |
$users = DB::table('users')->select('name','email')->get(); dd($users); |
打印结果如下: 4.2 获取单条记录 获取单条记录需要在查询基础上加上first方法:
1 2 |
$user = DB::table('users')->where('name','Laravel')->first(); dd($user); |
则对应结果为: 4.3 分组块获取数据 如果数据库包含多条数据,则一次性获取会极大影响性能,对应地,我们可以调用chunk方法分组块获取数据:
1 2 3 4 5 6 7 |
DB::table('users')->chunk(2,function($users){ foreach($users as $user){ // if($user->name=='LaravelAcademy') // return false; echo $user->name.'<br>'; } }); |
这里我们指定每次取两条记录。注释部分的意思是我们可以设定查询退出条件,当达到该条件时,查询退出,不再往下执行。 4.4 获取单列的值 上述方法获取的查询结果都是对象实例/对象实例数组,有时候,我们只是想简单获取单列的值,遍历数组获取指定列的值太麻烦,可以使用lists方法获取列值数组:
1 2 |
$users = DB::table('users')->lists('name'); dd($users); |
对应的输出为: 这样我们就可以免去遍历对象数组的麻烦。 4.5 原生表达式 此外,查询构建器还支持原生表达式,我们需要调用DB门面的raw方法来实现:
1 2 |
$users = DB::table('users')->select(DB::raw('name,email'))->where('id','<',3)->get(); dd($users); |
对应输出为: from:http://laravelacademy.org/post/908.html
View DetailsLaravel支持多种数据库,包括MySQL、Postgres、SQLite和SQL Server,在Laravel中连接数据库和查询数据库都非常简单,我们可以使用多种方式与数据库进行交互,包括原生SQL语句、查询构建器以及Eloquent ORM。本节我们先演示如何使用原生SQL在Laravel应用中对数据库进行增删改查。 1、连接数据库 Laravel中数据库配置文件为config/database.php,打开该文件,默认内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php return [ //默认返回结果集为PHP对象实例 'fetch' => PDO::FETCH_CLASS, //默认数据库连接为mysql,可以在.env文件中修改DB_CONNECTION的值 'default' => env('DB_CONNECTION', 'mysql'), 'connections' => [ //sqlite数据库相关配置 'sqlite' => [ 'driver' => 'sqlite', 'database' => storage_path('database.sqlite'), 'prefix' => '', ], //mysql数据库相关配置 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false, ], //Postgres数据库相关配置 'pgsql' => [ 'driver' => 'pgsql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', ], //SQL Server数据库相关配置 'sqlsrv' => [ 'driver' => 'sqlsrv', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'prefix' => '', ], ], //迁移表名称 'migrations' => 'migrations', //Redis数据库相关配置 'redis' => [ 'cluster' => false, 'default' => [ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], ], ]; |
如果要修改数据库配置信息,去修改.env对应值即可。我们实例教程使用的Homestead开发环境默认配置,不做修改。如果你没有使用Homestead,则需要根据本地配置修改相应配置值。 我们在讲Windows上安装Homestead的时候,已经演示过数据库连接测试,以及如何在本地使用Navicat连接Homestead的数据库,这里不再赘述,下面直接进入如何使用数据库进行增删改查。 2、数据库初始化 我们在项目根目录使用Artisan命令运行Laravel自带的迁移生成users表和password_reset表: 对应在数据库中生成三张表: 3、使用DB门面进行增删改查 3.1 插入数据 我们使用DB门面执行原生SQL语句,插入操作使用DB门面的insert方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; use DB; class TestController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )', [1, 'Laravel','laravel@test.com','123']); DB::insert('insert into users (id, name, email, password) values (?, ?, ?, ? )', [2, 'Academy','academy@test.com','123']); } } |
在浏览器中输入http://laravel.app:8000/test,执行成功后在数据库中插入两条记录: 3.2 查询语句 查询操作使用DB门面的select方法,代码如下:
1 2 |
$user = DB::select('select * from users where id = ?', [1]); dd($user); |
在浏览器地址栏输入http://laravel.app:8000/test,输出内容如下: 可以看到select查询返回的结果是数组。而数组中的每一个元素都是PHP对象。 我们还 可以看到在执行查询的时候使用了参数绑定,以避免SQL注入。除此之外还可以使用命名绑定:
1 |
$user = DB::select('select * from users where id = :id', [':id'=>1]); |
效果一样。 3.3 更新语句 更新表记录可以使用DB门面的update方法,该方法返回受影响的行数:
1 2 |
$affected = DB::update('update users set name="LaravelAcademy" where name = ?', ['Academy']); echo $affected; |
打印结果为1,如果没有更新任何记录则返回0。 3.4 删除语句 删除表记录使用DB门面的delete方法,和update类似,该方法返回被删除的行数:
1 2 |
$deleted = DB::delete('delete from users'); echo $deleted; |
打印结果为2,表数据都被我们删除了。 3.5 通用语句 除了上述这些DML(insert/update/delete)和DQL(select)语句,SQL语句还包括DCL(create/drop等)和DDL(grant等),要运行后者,我们可以调用DB门面的statement方法:
1 |
DB::statement('drop table users'); |
执行完该语句后,数据表users会被删除。 4、监听查询事件 除此之外,我们还可以通过DB门面的listen方法监听查询事件,比如我们在记录日志和调试的时候这会给我们确定问题提供便利,可以在服务提供者的boot方法中注册该监听器,例如我们在AppServiceProvider的boot方法中定义监听器如下:
1 2 3 4 5 6 7 8 9 10 11 |
/** * 启动所有应用服务 * * @return void */ public function boot() { DB::listen(function($sql, $bindings, $time) { echo 'SQL语句执行:'.$sql.',参数:'.json_encode($bindings).',耗时:'.$time.'ms'; }); } |
这样我们运行如下SQL语句:
1 2 |
DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )', [3, 'LaravelAcademy','laravel-academy@test.com','123']); |
则浏览器会输出如下内容:
1 |
SQL语句执行:insert into users (id, name, email, password) values (?, ?, ? , ? ),参数:[3,"LaravelAcademy","laravel-academy@test.com","123"],耗时:1.26ms |
5、数据库事务 很多时候,我们需要执行一连串操作,而其中任何一个操作出错则整个流程失败,需要回退重来,这个时候我们就要用到数据库事务。 DB门面提供两种方式支持数据库事务,一种是调用transaction方法然后传入闭包作为参数,我们将需要进行事务操作的逻辑放到闭包函数内:
1 2 3 4 |
DB::transaction(function () { DB::table('users')->update(['id' => 1]); DB::table('posts')->delete(); }); |
另一种是 beginTransaction、 rollBack和 commit三个方法一起使用从而构建一个完整的事务操作:
1 2 3 4 5 6 |
DB::beginTransaction(); if($somethingIsFailed){ DB::rollback(); return false; } DB::commit(); |
此外,使用DB门面提供的事务还支持查询构建器和Eloquent ORM数据库操作。 from:https://laravelacademy.org/post/854.html
View Details视图的基本使用很简单,可查看视图文档了解详情,这里我们演示两个使用示例:在视图间共享数据和视图Composer。 1、在视图间共享数据 除了在单个视图中传递指定数据之外,有时候需要在所有视图中传入同一数据,即我们需要在不同视图中共享数据。要实现这一目的,需要使用视图工厂的share方法。 全局帮助函数view和前面一节提到的response类似,如果传入参数,则返回Illuminate\View\View实例,不传入参数则返回Illuminate\View\Factory实例。所以我们可以通过在服务提供者的boot方法中使用如下方式实现视图间共享数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { //视图间共享数据 view()->share('sitename','<a title="View all posts in Laravel" href="http://laravelacademy.org/tags/laravel" target="_blank">Laravel</a>学院'); } /** * Register any application services. * * @return void */ public function register() { // } } |
我们在routes.php中定义两个路由:
1 2 3 4 5 6 7 |
Route::get('testViewHello',function(){ return view('hello'); }); Route::get('testViewHome',function(){ return view('home'); }); |
然后在resources/views目录下创建一个home.blade.php视图文件,内容如下:
1 |
{{$sitename}}首页 |
再创建一个hello.blade.php视图文件:
1 |
欢迎来到{{$sitename}}! |
在浏览器中分别访问http://laravel.app:8000/testViewHello和http://laravel.app:8000/testViewHome,则都能解析出$sitename的值。 2、视图Composer 有时候我们想要在每次视图渲染时绑定一些特定数据到视图中,比如登录用户信息,这时候我们就要用到视图Composer,视图Composer通过视图工厂的composer方法实现。该方法的第二个回调参数支持基于控制器动作和闭包函数两种方式。 简单起见,我们还是基于AppServiceProvider,不去单独创建服务提供者,这里我们传递闭包参数(控制器动作参考视图文档):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { //视图间共享数据 view()->share('sitename','Laravel学院'); //视图Composer view()->composer('hello',function($view){ $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg')); }); } /** * Register any application services. * * @return void */ public function register() { // } } |
修改hello.blade.php视图文件:
1 2 3 4 5 |
欢迎来到{{$sitename}}! <h3>用户信息</h3> 用户名:{{$user['name']}}<br> 用户头像:{{$user['avatar']}} |
在浏览器中访问http://laravel.app:8000/testViewHello,输出内容如下:
1 2 3 4 5 6 |
欢迎来到Laravel学院! 用户信息 用户名:test 用户头像:/path/to/test.jpg |
你也可以传递数据到多个视图:
1 2 3 |
view()->composer(['hello','home'],function($view){ $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg')); }); |
甚至所有视图(使用通配符*):
1 2 3 |
view()->composer('*',function($view){ $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg')); }); |
注:更多视图方法请参考Laravel核心源码:Illuminate\View\View.php及Illuminate\View\Factory.php。 from:http://laravelacademy.org/post/697.html
View Details1、Response篇 1.1 基本响应 最基本的HTTP响应只需在路由闭包或控制器动作中返回一个简单字符串即可,但是具体业务逻辑中大部分响应都是在控制器动作中返回Response实例或者视图。Response是继承自 Symfony\Component\HttpFoundation\Response的 Illuminate\Http\Response类的一个实例,我们可以使用该实例上的一系列方法来创建HTTP响应:
1 2 3 4 5 6 7 8 |
use Illuminate\Http\Response; Route::get('testResponse',function(){ $content = 'Hello <a title="View all posts in Laravel" href="http://laravelacademy.org/tags/laravel" target="_blank">Laravel</a>Academy!'; $status = 200; $value = 'text/html;charset=utf-8'; return (new Response($content,$status))->header('Content-Type',$value); }); |
在浏览器中访问,F12查看响应头信息(Chrome浏览器): 如果我们尝试修改$status=500,则头信息如下: 为方便使用,我们还可以使用全局帮助函数response来替代生成Response对象实例:
1 2 3 4 5 6 |
Route::get('testResponse',function(){ $content = 'Hello LaravelAcademy!'; $status = 500; $value = 'text/html;charset=utf-8'; return response($content,$status)->header('Content-Type',$value); }); |
效果一样。以后我们将默认使用这种方式,不再生成Response对象实例。 此外,需要注意的是,Illuminate\Http\Response 类中还使用了ResponseTrait,header方法正是该trait提供的,除了header之外,该trait还提供了withCookie、content和status方法。header方法用于设置响应头信息,withCookie方法用于添加cookie,这两个方法都会返回调用它的Response自身对象,所以这两个方法都支持方法链(即多次调用header或withCookie方法);而content和status方法则用于返回当前响应的响应实体内容和响应状态码。 1.2 添加Cookie 正如上面提到的,我们使用withCookie方法为响应添加cookie,由于header和withCookie支持方法链,所以我们可以这样使用:
1 2 3 4 5 6 7 |
Route::get('testResponseCookie',function(){ $content = 'Hello LaravelAcademy!'; $status = 200; $value = 'text/html;charset=utf-8'; return response($content,$status)->header('Content-Type',$value) ->withCookie('site','LaravelAcademy.org'); }); |
在浏览器中访问,F12查看Cookie信息: 我们还可以使用该cookie的有效期、作用域等信息:
1 2 3 4 5 6 7 8 |
Route::get('testResponseCookie',function(){ $content = 'Hello LaravelAcademy!'; $status = 200; $value = 'text/html;charset=utf-8'; //设置cookie有效期为30分钟,作用路径为应用根目录,作用域名为laravel.app return response($content,$status)->header('Content-Type',$value) ->withCookie('site','LaravelAcademy.org',30,'/','laravel.app'); }); |
注:withCookie方法实际上是调用了全局帮助函数cookie生成cookie,然后将cookie放到响应头中。 再次在浏览器中访问,F12查看cookie信息如下: 里面多出了有效期及作用域名等相关信息。 此外,我们还关注到该cookie是经过加密的,这一点我们在前面已经提到过,这是为了安全性考虑,如果要取消加密,在app/Http/Middleware/EncryptCookies.php文件中将对应的cookie名添加到EncryptCookies类属性$except中即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php namespace App\Http\Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter; class EncryptCookies extends BaseEncrypter { /** * 不被加密的cookie名 * * @var array */ protected $except = [ 'site' ]; } |
再次在浏览器中访问http://laravel.app:8000/testResponseCookie,F12查看Cookie信息如下: 当然,为了安全起见,我们不建议这么做。 2、ResponseFactory篇 response函数如果不传入参数会返回 Illuminate\Contracts\Routing\ResponseFactory契约的实现——Illuminate\Routing\ResponseFactory,该类中提供了多个方法用来生成更加丰富的响应类型,比如视图响应、JSON响应、文件下载等等。 2.1 视图响应 使用view方法即可返回一个视图作为响应内容:
1 2 3 4 5 |
Route::get('testResponseView',function(){ $value = 'text/html;charset=utf-8'; return response()->view('hello',['message'=>'Hello LaravelAcademy']) ->header('Content-Type',$value); }); |
与之对应的,我们需要在resources/views下新建一个视图文件hello.blade.php,其内容如下:
1 |
{{$message}} |
接下来我们在浏览器中访问http://laravel.app:8000/testResponseView,页面输出:
1 |
Hello LaravelAcademy |
还可以再简化,如果我们不需要自定义HTTP响应头,还可以直接使用全局帮助函数view:
1 2 3 4 |
Route::get('testResponseView',function(){ $value = 'text/html;charset=utf-8'; return view('hello',['message'=>'Hello LaravelAcademy'])); }); |
效果和上面一样。 2.2 返回JSON/JSONP 还可以使用json方法返回json格式数据:
1 2 3 |
Route::get('testResponseJson',function(){ return response()->json(['name'=>'LaravelAcademy','passwd'=>'LaravelAcademy.org']); }); |
在浏览器中访问,F12查看响应头信息: 根据输出信息可见,json方法会自动设置Content-Type为application/json,并调用PHP内置函数json_encode讲数组转化为json格式字符串。 如果返回的是JSONP响应,也很简单,只需要在json后面再调用setCallback即可:
1 2 3 4 |
Route::get('testResponseJson',function(){ return response()->json(['name'=>'LaravelAcademy','passwd'=>'LaravelAcademy.org']) ->setCallback(request()->input('callback')); }); |
2.3 文件下载 使用download方法可生成用于下载给定路径文件的响应,这里我们下载上一节上传的文件:
1 2 3 4 5 6 |
Route::get('testResponseDownload',function(){ return response()->download( realpath(base_path('public/images')).'/laravel-5-1.jpg', 'Laravel学院.jpg' ); }); |
在浏览器中访问http://laravel.app:8000/testResponseDownload,页面将会下载laravel-5-1.jpg文件并保存为Laravel学院.jpg。 3、 RedirectResponse篇 重定向响应是 Illuminate\Http\RedirectResponse类的实例,我们通常使用全局帮助函数redirect来生成 RedirectResponse实例。和response类似,redirect函数如果接收参数则调用的是Illuminate\Routing\Redirector类的to方法,如果无参调用则返回的是Redirector对象实例。 3.1 基本重定向
1 2 3 |
Route::get('dashboard', function () { return redirect('home/dashboard'); }); |
如果要重定向到上一个位置,则使用back方法:
1 2 3 4 |
Route::post('user/profile', function () { // 验证请求... return back()->withInput(); }); |
3.2 重定向到命名路由 使用route方法重定向到命名路由:
1 2 3 4 5 6 7 |
Route::get('/hello/laravelacademy',['as'=>'academy',function(){ return 'Hello LaravelAcademy'; }]); Route::get('testResponseRedirect',function(){ return redirect()->route('academy'); }); |
在浏览器中访问http://laravel.app:8000/testResponseRedirect,页面会跳转到http://laravel.app:8000/hello/laravelacademy并输出:
1 |
Hello LaravelAcademy |
如果命名路由中有参数,那么我们可以在route中传入参数:
1 2 3 4 5 6 7 |
Route::get('/hello/laravelacademy/{id}',['as'=>'academy',function($id){ return 'Hello LaravelAcademy '.$id; }]); Route::get('testResponseRedirect',function(){ return redirect()->route('academy',100); }); |
在浏览器中访问http://laravel.app:8000/testResponseRedirect,页面会跳转到http://laravel.app:8000/hello/laravelacademy/100并输出:
1 |
Hello LaravelAcademy 100 |
3.3 重定向到控制器动作 使用action方法重定向到控制器动作:
1 2 3 4 5 |
Route::resource('post','PostController'); Route::get('testResponseRedirect',function(){ return redirect()->action('PostController@index'); }); |
在浏览器中访问http://laravel.app:8000/testResponseRedirect,则页面会跳转到http://laravel.app:8000/post并输出对应内容。 当然也可以传递参数到action方法:
1 2 3 |
Route::get('testResponseRedirect',function(){ return redirect()->action('PostController@show',[1]); }); |
3.4 带一次性Session数据的重定向 使用with方法可以携带一次性session数据到重定向请求页面(一次性session数据即使用后立即销毁的session数据项):
1 2 3 4 |
Route::post('user/profile', function () { // 更新用户属性... return redirect('dashboard')->with('status', 'Profile updated!'); }); |
这种特性通常在提交表单验证失败返回错误信息时很有用。 from:http://laravelacademy.org/post/623.html
View Details1、获取Request请求实例 Laravel中一般通过控制器方法依赖注入来获取当前请求的Request实例。 我们通过定义一个隐式控制器来进行本章节的测试。首先我们在routes.php定义路由如下:
1 2 |
Route::controller('request','RequestController'); <span style="color: #ff0000;">注:在laravel5.4中不再支持这种路由写法了。</span> |
然后我们在app/Http/Controllers下创建一个控制器RequestController.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\Response; use App\Http\Requests; use App\Http\Controllers\Controller; class RequestController extends Controller { public function getBasetest(Request $request) { $input = $request->input('test'); echo $input; } } |
要访问getBasetest方法,我们只需在浏览器中访问http://laravel.app:8000/request/basetest?test=laravelacademy,这样页面会输出:
1 |
laravelacademy |
2、获取请求URL及请求方法 2.1 获取请求URL路径 要获取当前请求的URL,我们可以通过Request实例上的path方法,需要注意的是该方法返回相对请求路径,如果要获取绝对请求路径,可以通过Request实例上的另一个方法——url:
1 2 3 4 5 6 7 8 9 10 11 12 |
public function getUrl(Request $request) { //匹配request/*的URL才能继续访问 if(!$request->is('request/*')){ abort(404); } $uri = $request->path(); $url = $request->url(); echo $uri; echo '<br>'; echo $url; } |
我们在浏览器中访问http://laravel.app:8000/request/url,页面输入如下内容:
1 2 |
request/url http://laravel.app:8000/request/url |
2.2 获取请求方法 我们还可以通过调用Request实例上的getMethod方法获取当前请求的方法:
1 2 3 4 5 6 7 8 |
public function getMethod(Request $request){ //非get请求不能访问 if(!$request->isMethod('get')){ abort(404); } $method = $request->method(); echo $method; } |
3、获取请求数据 3.1 当前请求输入 使用Request实例上的input方法即可获取请求输入数据。该方法可以接收两个参数,第一个参数是传递参数名称,第二个参数是如果参数名为空返回的默认值,此外该方法还支持获取数组参数对应值,我们定义测试方法如下:
1 2 3 4 5 6 7 |
public function getInputData(Request $request){ //获取GET方式传递的name参数,默认为LaravelAcademy $name = $request->input('name','LaravelAcademy'); echo $name; echo '<br>'; echo $request->input('test.0.name'); } |
在浏览器中输入http://laravel.app:8000/request/input-data?name=Laravel&test[][name]=Academy,则页面输出:
1 2 |
Laravel Academy |
如果我们想要在获取输入值之前判断输入参数名是否存在,可以使用has方法,比如我们想要判断输入参数是否包含hello,可使用如下方法:
1 2 |
if($request->has('hello')) echo $request->input('hello'); |
想要获取所有输入参数值,可以使用Request实例上的all方法;想要获取部分输入值,可使用only方法;想要排除部分输入参数值,可使用except方法:
1 2 3 4 5 6 7 8 9 10 11 |
public function getInputData(Request $request){ $allData = $request->all(); $onlyData = $request->only('name','hello'); $exceptData = $request->except('hello'); echo '<pre>'; print_r($allData); print_r($onlyData); print_r($exceptData); } |
在浏览器中访问http://laravel.app:8000/request/input-data?name=Laravel&test[][name]=Academy&hello=World,页面输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Array ( [name] => Laravel [test] => Array ( [0] => Array ( [name] => Academy ) ) [hello] => World ) Array ( [name] => Laravel [hello] => World ) Array ( [name] => Laravel [test] => Array ( [0] => Array ( [name] => Academy ) ) ) |
3.2 上一次请求输入 上面的方法都是用于获取当前请求的输入,如果想要获取上一次请求的输入,需要在处理上一次请求时使用Request实例上的flash方法将请求数据暂时保存到session中,然后在当前请求中使用Request实例上的old方法获取session中保存的数据,获取到数据后就会将session中保存的请求数据销毁:
1 2 3 4 5 6 7 8 9 |
public function getLastRequest(Request $request){ $request->flash(); } public function getCurrentRequest(Request $request){ $lastRequestData = $request->old(); echo '<pre>'; print_r($lastRequestData); } |
如果我们想要在上次请求保存数据后重定向到当前请求URL,则可以使用如下方式定义getLastRequest方法:
1 2 3 4 |
public function getLastRequest(Request $request){ //$request->flash(); return redirect('/request/current-request')->withInput(); } |
这样我们在浏览器中访问http://laravel.app:8000/request/last-request?name=test&passwd=123456, 页面会重定向到http://laravel.app:8000/request/current-request并输出如下内容:
1 2 3 4 5 |
Array ( [name] => test [passwd] => 123456 ) |
再次刷新页面,输出为空:
1 2 3 |
Array ( ) |
则表明取出数据后session中的请求数据被清空。更多方法使用参考HTTP请求官方文档。 4、获取Cookie数据 我们可以使用Request实例上的cookie方法获取cookie数据,该方法可以接收一个参数名返回对应的cookie值,如果不传入参数,默认返回所有cookie值:
1 2 3 4 |
public function getCookie(Request $request){ $cookies = $request->cookie(); dd($cookies); } |
我们在浏览器中访问http://laravel.app:8000/request/cookie,页面输出:
1 2 3 4 |
array:2 [ "XSRF-TOKEN" => "<span title="40 characters">fSP1erkCxnxX0wCyrJWJuR3ruH8c09VZXnR64nbC" "laravel_session" => "<span title="40 characters">820e88a52c45f8dbda55e8c6aaaa9bbca2c760ef" ]</span></span> |
我们可以调用Response实例上的withCookie方法新增cookie:
1 2 3 4 5 6 7 8 |
public function getAddCookie(){ $response = new Response(); //第一个参数是cookie名,第二个参数是cookie值,第三个参数是有效期(分钟) $response->withCookie(cookie('website','LaravelAcademy.org',1)); //如果想要cookie长期有效使用如下方法 //$response->withCookie(cookie()->forever('name', 'value')); return $response; } |
我们重新定义getCookie方法如下:
1 2 3 4 5 |
public function getCookie(Request $request){ $cookie = $request->cookie('website'); echo $cookie; } |
接下来我们在浏览器中访问http://laravel.app:8000/request/add-cookie,再访问http://laravel.app:8000/request/cookie,页面输出如下:
1 |
LaravelAcademy.org |
5、上传文件 我们定义文件上传页面及上传处理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//文件上传表单 public function getFileupload() { $postUrl = '/request/fileupload'; $csrf_field = csrf_field(); $html = <<<CREATE <form action="$postUrl" method="POST" enctype="multipart/form-data"> $csrf_field <input type="file" name="file"><br/><br/> <input type="submit" value="提交"/> </form> CREATE; return $html; } //文件上传处理 public function postFileupload(Request $request){ //判断请求中是否包含name=file的上传文件 if(!$request->hasFile('file')){ exit('上传文件为空!'); } $file = $request->file('file'); //判断文件上传过程中是否出错 if(!$file->isValid()){ exit('文件上传出错!'); } $destPath = realpath(public_path('images')); if(!file_exists($destPath)) mkdir($destPath,0755,true); $filename = $file->getClientOriginalName(); if(!$file->move($destPath,$filename)){ exit('保存文件失败!'); } exit('文件上传成功!'); } |
通过以上代码可以看到我们可以使用Request实例上的file方法获取上传文件实例,该方法接收的参数是上传文件input标签的name属性,该文件上传实例是 Symfony\Component\HttpFoundation\File\UploadedFile类的实例,更多有关该实例的可用方法,可参考UploadedFile的API文档。 from:http://laravelacademy.org/post/606.html
View Details基本控制器及控制器路由、控制器中间件都比较简单,这里不再赘述,相关文档参考HTTP 控制器文档一节。 1、创建RESTFul风格控制器 注:关于什么是RESTFul风格及其规范可参考这篇文章:理解RESTful架构。 本文我们主要讨论创建一个RESTFul风格的控制器用于对博客文章进行增删改查,创建这样的控制器很简单,在应用根目录运行如下Artisan命令即可:
1 |
php artisan make:controller PostController |
该命令会在app/Http/Controllers目录下生成一个PostController.php文件,该控制器内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class PostController extends Controller { /** * 显示文章列表. * * @return Response */ public function index() { // } /** * 创建新文章表单页面 * * @return Response */ public function create() { // } /** * 将新创建的文章存储到存储器 * * @param Request $request * @return Response */ public function store(Request $request) { // } /** * 显示指定文章 * * @param int $id * @return Response */ public function show($id) { // } /** * 显示编辑指定文章的表单页面 * * @param int $id * @return Response */ public function edit($id) { // } /** * 在存储器中更新指定文章 * * @param Request $request * @param int $id * @return Response */ public function update(Request $request, $id) { // } /** * 从存储器中移除指定文章 * * @param int $id * @return Response */ public function destroy($id) { // } } |
2、为RESTFul风格控制器注册路由 接下来我们在routes.php文件中为该控制器注册路由:
1 |
Route::resource('post','PostController'); |
该路由包含了指向多个动作的子路由: 方法 路径 动作 路由名称 GET /post index post.index GET /post/create create post.create POST /post store post.store GET /post/{post} show post.show GET /post/{post}/edit edit post.edit PUT/PATCH /post/{post} update post.update DELETE /post/{post} destroy post.destroy 比如我们在浏览器中以GET方式访问http://laravel.app:8000/post,则访问的是PostController的index方法,我们可以通过route('post.index')生成对应路由URL。类似的,如果我们以POST方式访问http://laravel.app:8000/post,则访问的是PostController的store方法,对应的POST表单action属性值则可以通过route('post.store')来生成。 3、实例教程——文章增删改查 接下来我们演示基本的增删改查操作,关于数据库的操作我们后面再讲,这里我们使用缓存作为存储器(Laravel默认使用文件缓存)。 注意:我们这里用到了Cache门面,使用前不要忘了在PostController顶部使用use Cache;引入。关于Cache的用法,可参考缓存文档。 3.1 新增文章 首先我们新增一篇文章,定义PostController控制器的create方法和store方法如下(视图部门我们放到后面讲,这里就将HTML放到PHP变量里):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/** * 创建新文章表单页面 * * @return Response */ public function create() { $postUrl = route('post.store'); $csrf_field = csrf_field(); $html = <<<CREATE <form action="$postUrl" method="POST"> $csrf_field <input type="text" name="title"><br/><br/> <textarea name="content" cols="50" rows="5"></textarea><br/><br/> <input type="submit" value="提交"/> </form> CREATE; return $html; } /** * 将新创建的文章存储到存储器 * * @param Request $request * @return Response */ public function store(Request $request) { $title = $request->input('title'); $content = $request->input('content'); $post = ['title'=>trim($title),'content'=>trim($content)]; $posts = Cache::get('posts',[]); if(!Cache::get('post_id')){ Cache::add('post_id',1,60); }else{ Cache::increment('post_id',1); } $posts[Cache::get('post_id')] = $post; Cache::put('posts',$posts,60); return redirect()->route('post.show',['post'=>Cache::get('post_id')]); } |
3.2 查看文章 访问http://laravel.app:8000/post/create页面,填写表单,点击“提交”,保存成功后,页面跳转到详情页:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/** * 显示指定文章 * * @param int $id * @return Response */ public function show($id) { $posts = Cache::get('posts',[]); if(!$posts || !$posts[$id]) exit('Nothing Found!'); $post = $posts[$id]; $editUrl = route('post.edit',['post'=>$id]); $html = <<<DETAIL <h3>{$post['title']}</h3> <p>{$post['content']}</p> <p> <a href="{$editUrl}">编辑</a> </p> DETAIL; return $html; } |
3.3 编辑文章 同理我们定义编辑文章对应的edit方法和update方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/** * 显示编辑指定文章的表单页面 * * @param int $id * @return Response */ public function edit($id) { $posts = Cache::get('posts',[]); if(!$posts || !$posts[$id]) exit('Nothing Found!'); $post = $posts[$id]; $postUrl = route('post.update',['post'=>$id]); $csrf_field = csrf_field(); $html = <<<UPDATE <form action="$postUrl" method="POST"> $csrf_field <input type="hidden" name="_method" value="PUT"/> <input type="text" name="title" value="{$post['title']}"><br/><br/> <textarea name="content" cols="50" rows="5">{$post['content']}</textarea><br/><br/> <input type="submit" value="提交"/> </form> UPDATE; return $html; } /** * 在存储器中更新指定文章 * * @param Request $request * @param int $id * @return Response */ public function update(Request $request, $id) { $posts = Cache::get('posts',[]); if(!$posts || !$posts[$id]) exit('Nothing Found!'); $title = $request->input('title'); $content = $request->input('content'); $posts[$id]['title'] = trim($title); $posts[$id]['content'] = trim($content); Cache::put('posts',$posts,60); return redirect()->route('post.show',['post'=>Cache::get('post_id')]); } |
3.4 删除文章 我们还可以使用destroy方法删除文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * 从存储器中移除指定文章 * * @param int $id * @return Response */ public function destroy($id) { $posts = Cache::get('posts',[]); if(!$posts || !$posts[$id]) exit('Nothing Deleted!'); unset($posts[$id]); Cache::decrement('post_id',1); return redirect()->route('post.index'); } |
要删除文章,需要参考编辑表单伪造删除表单方法为DELETE(一般使用AJAX删除),这里不再演示。 3.5 文章列表 最后我们再来定义一个用于显示所有文章列表的index方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/** * 显示文章列表. * * @return Response */ public function index() { $posts = Cache::get('posts',[]); if(!$posts) exit('Nothing'); $html = '<ul>'; foreach ($posts as $key=>$post) { $html .= '<li><a href='.route('post.show',['post'=>$key]).'>'.$post['title'].'</li>'; } $html .= '</ul>'; return $html; } from:<a href="http://laravelacademy.org/post/549.html">http://laravelacademy.org/post/549.html</a> |