From 8d53f04a9803bfc3e81b048d748190419fe8b9d5 Mon Sep 17 00:00:00 2001 From: yexingzhe Date: Fri, 22 Aug 2014 17:09:38 +0800 Subject: [PATCH] --- cn/cache.md | 144 ++++++ cn/commands.md | 139 +++++ cn/contributing.md | 3 + cn/controllers.md | 195 +++++++ cn/database.md | 133 +++++ cn/documentation.md | 2 +- cn/eloquent.md | 1199 +++++++++++++++++++++++++++++++++++++++++++ cn/errors.md | 119 +++++ cn/events.md | 175 +++++++ cn/extending.md | 235 +++++++++ cn/facades.md | 158 ++++++ cn/helpers.md | 412 +++++++++++++++ cn/html.md | 199 +++++++ cn/ioc.md | 186 +++++++ cn/license.md | 8 + cn/lifecycle.md | 81 +++ cn/localization.md | 105 ++++ cn/mail.md | 140 +++++ cn/migrations.md | 112 ++++ cn/packages.md | 237 +++++++++ cn/pagination.md | 140 +++++ cn/queries.md | 314 +++++++++++ cn/queues.md | 255 +++++++++ cn/quick.md | 66 +-- cn/redis.md | 81 +++ cn/releases.md | 2 +- cn/requests.md | 214 ++++++++ cn/responses.md | 223 ++++++++ cn/routing.md | 360 +++++++++++++ cn/schema.md | 218 ++++++++ cn/security.md | 312 +++++++++++ cn/session.md | 114 ++++ cn/ssh.md | 263 ++++++++++ cn/templates.md | 182 +++++++ cn/testing.md | 221 ++++++++ cn/validation.md | 559 ++++++++++++++++++++ 36 files changed, 7472 insertions(+), 34 deletions(-) create mode 100644 cn/cache.md create mode 100644 cn/commands.md create mode 100644 cn/contributing.md create mode 100644 cn/controllers.md create mode 100644 cn/database.md create mode 100644 cn/eloquent.md create mode 100644 cn/errors.md create mode 100644 cn/events.md create mode 100644 cn/extending.md create mode 100644 cn/facades.md create mode 100644 cn/helpers.md create mode 100644 cn/html.md create mode 100644 cn/ioc.md create mode 100644 cn/license.md create mode 100644 cn/lifecycle.md create mode 100644 cn/localization.md create mode 100644 cn/mail.md create mode 100644 cn/migrations.md create mode 100644 cn/packages.md create mode 100644 cn/pagination.md create mode 100644 cn/queries.md create mode 100644 cn/queues.md create mode 100644 cn/redis.md create mode 100644 cn/requests.md create mode 100644 cn/responses.md create mode 100644 cn/routing.md create mode 100644 cn/schema.md create mode 100644 cn/security.md create mode 100644 cn/session.md create mode 100644 cn/ssh.md create mode 100644 cn/templates.md create mode 100644 cn/testing.md create mode 100644 cn/validation.md diff --git a/cn/cache.md b/cn/cache.md new file mode 100644 index 0000000..a5a138f --- /dev/null +++ b/cn/cache.md @@ -0,0 +1,144 @@ +# 缓存 + +- [配置](#configuration) +- [缓存用法](#cache-usage) +- [增加 & 减少](#increments-and-decrements) +- [缓存标签](#cache-tags) +- [数据库缓存](#database-cache) + + +## 配置 + +Laravel 对不同的缓存机制提供了一套统一的API。缓存配置信息存放于`app/config/cache.php`文件。在该配置文件中,你可以指定整个应用程序所使用的缓存驱动器。Laravel自身支持大多数流行的缓存服务器,例如[Memcached](http://memcached.org)和[Redis](http://redis.io)。 + +缓存配置文件还包含了其他配置项,文件里都有详细说明,因此,请务必查看这些配置项和其描述信息。默认情况下,Laravel被配置为使用`file`缓存驱动,它将数据序列化,并存放于文件系统中。在大型应用中,强烈建议使用基于内存的缓存系统,例如Memcached或APC。 + + +## 缓存用法 + +#### 将某一数据存入缓存 + + Cache::put('key', 'value', $minutes); + +#### Using Carbon Objects To Set Expire Time + + $expiresAt = Carbon::now()->addMinutes(10); + + Cache::put('key', 'value', $expiresAt); + +#### 当某一数据不在缓存中是才将其保存 + + Cache::add('key', 'value', $minutes); + +如果该项实际上 **已经添加** 到缓存中,那么 `add` 方法将返回 `true` 。否则,此方法将返回 `false`。 + +#### 检查缓存中是否有某个key对应的数据 + + if (Cache::has('key')) + { + // + } + +#### 从缓存中取得数据 + + $value = Cache::get('key'); + +#### 从缓存中取得数据,如果数据不存,则返回指定的默认值 + + $value = Cache::get('key', 'default'); + + $value = Cache::get('key', function() { return 'default'; }); + +#### 将数据永久地存于缓存中 + + Cache::forever('key', 'value'); + +有时你可能想从缓存中取得某项数据,但是还希望在数据不存在时存储一项默认值。那就可以通过 `Cache::remember`方法实现: + + $value = Cache::remember('users', $minutes, function() + { + return DB::table('users')->get(); + }); + +还可以将`remember`和`forever`方法结合使用: + + $value = Cache::rememberForever('users', function() + { + return DB::table('users')->get(); + }); + +注意:所有存在于缓存中的数据都是经过序列化的,因此,你可以存储任何类型的数据。 + +#### Pulling An Item From The Cache + +If you need to retrieve an item from the cache and then delete it, you may use the `pull` method: + + $value = Cache::pull('key'); + +#### 从缓存中删除某项数据 + + Cache::forget('key'); + + +## 增加 & 减少 + +除了`文件`和`数据库`驱动器,其他驱动器都支持`增加`和`减少`操作: + +#### 让某个值增加 + + Cache::increment('key'); + + Cache::increment('key', $amount); + +#### 让某个值减少 + + Cache::decrement('key'); + + Cache::decrement('key', $amount); + + +## 缓存标签 + +> **注意:** 当使用 `file` 或者 `database` 缓存驱动时,是不支持缓存标签的。此外,在使用多个缓存标签时它们将存储为 "forever"。使用一个如 `memcached` 的驱动性能将会是最好的,它会自动清除过时的记录。 + +#### 访问一个标记的缓存 + +缓存标签允许你在缓存中标记相关的项目,然后刷新指定名称标签的所有缓存。要访问标记的缓存,请使用 `tags` 方法: + +你可以通过传递标签名称的有序列表或数组作为参数,来存储一个标记的缓存。 + + Cache::tags('people', 'authors')->put('John', $john, $minutes); + + Cache::tags(array('people', 'artists'))->put('Anne', $anne, $minutes); + +你可以在所有缓存存储方法中使用标签,包括 `remember`,`forever`,和 `rememberForever`。你也可以从缓存标签来访问已缓存的项目,以及使用其它缓存方法如 `increment` 和 `decrement`: + +#### 从标记的缓存中访问条目 + +通过与保存时所用相同的标签,作为参数列表来访问标记的缓存。 + + $anne = Cache::tags('people', 'artists')->get('Anne'); + + $john = Cache::tags(array('people', 'authors'))->get('John'); + +你可以通过标签名称(或名称列表)来刷新所有相关的缓存项。例如,下面的语句将移除所有标签中包含 `people` 和 `authors` 的缓存项。因此无论是之前例子中的 "Anne" 还是 "John" 都将从缓存中移除: + + Cache::tags('people', 'authors')->flush(); + +相比之下,下面的语句将移除标签中仅包含 'authors' 的缓存项,因此 "John" 将被移除,但不影响 "Anne" 。 + + Cache::tags('authors')->flush(); + + +## 数据库缓存 + +当使用`数据库`缓存驱动时,你需要设置一个数据表来缓存数据。以下案例是`Schema`表的定义: + + Schema::create('cache', function($table) + { + $table->string('key')->unique(); + $table->text('value'); + $table->integer('expiration'); + }); + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/commands.md b/cn/commands.md new file mode 100644 index 0000000..9adf54e --- /dev/null +++ b/cn/commands.md @@ -0,0 +1,139 @@ +# Artisan 开发 + +- [简介](#introduction) +- [构建命令](#building-a-command) +- [注册命令](#registering-commands) +- [调用其他命令](#calling-other-commands) + + +## 简介 + +除了 Artisan 已经提供的命令,你也可以为应用程序构建自己的命令。你可以将自己定制的命令保存在 `app/commands` 目录下,同样,你也可以选择任意的存储目录,只要你的命令能够按照 `composer.json` 中的设置被自动加载即可。 + + +## 构建命令 + +### 生成类 + +为了创建一个新命令,你可以使用Artisan中的 `command:make` 命令生成一个骨架作为你的起点: + +#### 生成一个命令类 + + php artisan command:make FooCommand + +默认情况下,生成的类文件被存放在 `app/commands` 目录下,同时你也可以指定自定义目录和命名空间: + + php artisan command:make FooCommand --path=app/classes --namespace=Classes + +当创建命令时,可以使用 `--command` 选项来指定终端命令的名称: + + php artisan command:make AssignUsers --command=users:assign + +### 实现命令 + +一旦命令被生成,你应当填写这个类的 `name` 和 `description` 属性,这些属性将在展示命令的 `list` 页面显示出来。 + +当命令运行的时候 `fire` 函数将被调用。你可以在这个函数里实现任何业务逻辑。 + +### 参数 & 选项 + +可以通过 `getArguments` 和 `getOptions` 函数为命令定义任何接受的参数和选项。这两个函数都将返回一个数组。 + +当定义 `arguments`,数组定义值表示如下: + + array($name, $mode, $description, $defaultValue) + +参数 `mode` 的值可以是 `InputArgument::REQUIRED`, `InputArgument::OPTIONAL` 中的任意一个。 + +当定义 `options`,数组定义值表示如下: + + array($name, $shortcut, $mode, $description, $defaultValue) + +对于选项,参数 `mode` 的值可以是`InputOption::VALUE_REQUIRED`, `InputOption::VALUE_OPTIONAL`, `InputOption::VALUE_IS_ARRAY`, `InputOption::VALUE_NONE` 中的一个。 + +`VALUE_IS_ARRAY` 模式表明,该开关可用于多次调用该命令时: + + php artisan foo --option=bar --option=baz + +`VALUE_NONE` 选项表明该选项值只能用来作为一个开关: + + php artisan foo --option + +### 获取输入 + +当命令执行的时候,你显然需要获取该命令所接收的参数和选项。要做到这一点,你可以使用 `argument` 和 `option` 函数: + +#### 获取一个参数的值 + + $value = $this->argument('name'); + +#### 获取所有参数 + + $arguments = $this->argument(); + +#### 获取一个选项的值 + + $value = $this->option('name'); + +获取所有选项 + + $options = $this->option(); + +### 输出 + +你可以使用`info`、`comment`、`question` 和 `error`方法将输出发送到控制台。这些函数中的每一个将根据它们的目的使用合适的 ANSI 颜色。 + +#### 发送信息到终端 + + $this->info('Display this on the screen'); + +#### 发送错误消息到终端 + + $this->error('Something went wrong!'); + +### 询问输入 + +你可以使用 `ask` 和 `confirm` 函数提示用户输入: + +#### 询问用户输入 + + $name = $this->ask('What is your name?'); + +#### 询问用户输入密码 + + $password = $this->secret('What is the password?'); + +#### 询问用户确认 + + if ($this->confirm('Do you wish to continue? [yes|no]')) + { + // + } + +你也可以为 `confirm` 指定一个默认值,应该是 `true` 或者 `false`: + + $this->confirm($question, true); + + +## 注册命令 + +#### 注册一个 Artisan 命令 + +一旦你的命令完成后,你需要使用 Artisan 进行注册,这样才能够被使用。这通常在 `app/start/artisan.php` 文件中完成。在这个文件中,你可以使用 `Artisan::add` 函数注册命令: + +**注册一个 Artisan 命令** + + Artisan::add(new CustomCommand); + +#### 注册一个在 IoC 容器中的命令 + +如果你的命令在应用程序的 [IoC 容器](/docs/ioc) 中注册,你可以使用 `Artisan::resolve` 函数使它对 Artisan 可用: + + Artisan::resolve('binding.name'); + + +## 调用其他命令 + +有时你可能希望在你的命令中调用其他命令,你可以通过使用 `call` 函数实现: + + $this->call('command.name', array('argument' => 'foo', '--option' => 'bar')); diff --git a/cn/contributing.md b/cn/contributing.md new file mode 100644 index 0000000..4d8e942 --- /dev/null +++ b/cn/contributing.md @@ -0,0 +1,3 @@ +# Contribution Guidelines + +If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 4.1 would be submitted to the `4.1` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. \ No newline at end of file diff --git a/cn/controllers.md b/cn/controllers.md new file mode 100644 index 0000000..3423007 --- /dev/null +++ b/cn/controllers.md @@ -0,0 +1,195 @@ +# 控制器 + +- [基础控制器](#basic-controllers) +- [控制器过滤器](#controller-filters) +- [RESTful 控制器](#restful-controllers) +- [资源控制器](#resource-controllers) +- [处理空方法](#handling-missing-methods) + + +## 基础控制器 (Basic Controllers) + +与其把所有路由逻辑写在一个 `routes.php` 文件中,你也许更希望用控制器类来组织它们。控制器可以把相关的路由逻辑组织在一个类中,而且可以使用由框架提供的更为强大的功能,比如自动[依赖注入](/docs/ioc)。 + +控制器一般储存在 `app/controllers` 目录下,这个目录默认已经被注册在 `composer.json` 文件的 `classmap` 属性中。However, controllers can technically live in any directory or any sub-directory. Route declarations are not dependent on the location of the controller class file on disk. So, as long as Composer knows how to autoload the controller class, it may be placed anywhere you wish. + +这是一个基础控制器的例子: + + class UserController extends BaseController { + + /** + * Show the profile for the given user. + */ + public function showProfile($id) + { + $user = User::find($id); + + return View::make('user.profile', array('user' => $user)); + } + + } + +所有控制器需要继承 `BaseController` 类。 `BaseController` 类也储存在 `app/controllers` 下,通常用来放置公用的控制器逻辑。 `BaseController` 类继承自框架的 `Controller` 类。现在,你可以在路由中像这样调用控制器操作: + + Route::get('user/{id}', 'UserController@showProfile'); + +如果你需要使用PHP命名空间嵌套组织控制器,你可以在定义路由时使用类名的全称: + + Route::get('foo', 'Namespace\FooController@method'); + +> **注意:** 由于我们采用 [Composer](http://getcomposer.org) 来自动加载PHP的类, 只要 composer 知道如何加载控制器文件,这些文件可以放在文件系统的任何地方。对于控制器的目录结构没有任何限定。路由与控制器基于文件系统的耦合关系已经完全解除了。 + +你也可以在控制器路由中指定名称: + + Route::get('foo', array('uses' => 'FooController@method', + 'as' => 'name')); + +你可以使用 `URL::action` 方法或`action`快捷方法获取一个控制器操作的URL: + + $url = URL::action('FooController@method'); + + $url = action('FooController@method'); + +你可以使用 `currentRouteAction` 方法获取当前控制器操作的名称: + + $action = Route::currentRouteAction(); + + +## 控制器过滤器 (Controller Filters) + +[过滤器](/docs/routing#route-filters)可以在控制器路由中指定,就像这样一个"标准的"路由: + + Route::get('profile', array('before' => 'auth', + 'uses' => 'UserController@showProfile')); + +不过,你也可以在控制器内部指定过滤器: + + class UserController extends BaseController { + + /** + * Instantiate a new UserController instance. + */ + public function __construct() + { + $this->beforeFilter('auth'); + + $this->beforeFilter('csrf', array('on' => 'post')); + + $this->afterFilter('log', array('only' => + array('fooAction', 'barAction'))); + } + + } + +你也可以使用闭包来指定内联的控制器过滤器: + + class UserController extends BaseController { + + /** + * Instantiate a new UserController instance. + */ + public function __construct() + { + $this->beforeFilter('auth', array('except' => 'getLogin')); + + $this->beforeFilter('csrf', array('on' => 'post')); + + $this->afterFilter('log', array('only' => + array('fooAction', 'barAction'))); + } + + } + + +## RESTful 控制器 (RESTful Controllers) + +Laravel框架中,你可以使用简单的REST命名规范,轻松定义单个路由去处理控制器的每个操作。首先,使用 `Route::controller` 方法定义路由: + + Route::controller('users', 'UserController'); + +`controller` 方法接受两个参数。第一个是基础URI控制器句柄,第二个是控制器的类名。接下来,就可以在控制器中添加带有相应HTTP动词前缀的方法: + + class UserController extends BaseController { + + public function getIndex() + { + // + } + + public function postProfile() + { + // + } + + } + + `index` 方法会应答带有这个控制器句柄的根URI(例如这个例子里的是 `users` )。 + +如果你的控制器操作名称包含多个单词,你可以使用 "破折号" 语法来获得URI。例如,下面 `UserController` 控制器中的这个操作会用来应答 `users/admin-profile` URI: + + public function getAdminProfile() {} + + +## 资源控制器 (Resource Controllers) + +资源控制器让围绕资源构建RESTful模式控制器变得更简单。比如,你可能希望创建一个的控制器,用来管理通过你的应用储存的图片( "photos" )。通过Artisan命令行输入 `controller:make` 命令以及路由中的 `Route::resource` 方法快速创建一个控制器。 + +如果要通过命令行创建控制器,使用如下命令: + + php artisan controller:make PhotoController + +现在我们可以为这个控制器注册一个资源模式的路由: + + Route::resource('photo', 'PhotoController'); + +这一个路由声明创建了多个路由规则,用来处理各种图像(photo)资源的RESTful操作。同样地,刚刚创建的控制器中已经包含了许多对应的方法。每个方法都带有注释说明,告诉你分别是用来处理什么URI和HTTP动词的。 + +#### 资源控制器中不同操作的用途 + +Verb | Path | Action | Route Name +----------|-----------------------------|--------------|--------------------- +GET | /resource | index | resource.index +GET | /resource/create | create | resource.create +POST | /resource | store | resource.store +GET | /resource/{resource} | show | resource.show +GET | /resource/{resource}/edit | edit | resource.edit +PUT/PATCH | /resource/{resource} | update | resource.update +DELETE | /resource/{resource} | destroy | resource.destroy + +有时你也许只需要处理其中一部分资源操作: + + php artisan controller:make PhotoController --only=index,show + + php artisan controller:make PhotoController --except=index + +同样,你也许需要在路由中制定那一部分要处理的操作: + + Route::resource('photo', 'PhotoController', + array('only' => array('index', 'show'))); + + Route::resource('photo', 'PhotoController', + array('except' => array('create', 'store', 'update', 'destroy'))); + +By default, all resource controller actions have a route name; however, you can override these names by passing a `names` array with your options: + + Route::resource('photo', 'PhotoController', + array('names' => array('create' => 'photo.build'))); + +#### Adding Additional Routes To Resource Controllers + +If it becomes necessary for you to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to `Route::resource`: + + Route::get('photos/popular'); + Route::resource('photos', 'PhotoController'); + + +## 处理空方法 (Handling Missing Methods) + +当控制器中没有任何方法匹配请求时,就会调用一个全局响应的方法。这个方法命名为 `missingMethod` ,它接收请求的参数数组作为方法的唯一参数。 + +#### 定义一个空方法 + + public function missingMethod($parameters = array()) + { + // + } \ No newline at end of file diff --git a/cn/database.md b/cn/database.md new file mode 100644 index 0000000..be1b8ff --- /dev/null +++ b/cn/database.md @@ -0,0 +1,133 @@ +# 数据库使用基础 + +- [配置](#configuration) +- [Read / Write Connections](#read-write-connections) +- [执行查询语句](#running-queries) +- [事务](#database-transactions) +- [同时使用多个数据库系统](#accessing-connections) +- [查询日志](#query-logging) + + +## 配置 + +在Laravel中连接和使用数据库非常简单。 数据库配置在 `app/config/database.php` 文件中. 所有受支持的数据库系统都列在了配置文件中,在配置文件中可以同时配置多个数据库系统的连接信息, 并指定默认使用哪个数据库连接。 + +Laravel 目前支持四种数据库系统,分别是: MySQL, Postgres, SQLite, 和 SQL Server。 + + +## Read / Write Connections + +Sometimes you may wish to use one database connection for SELECT statements, and another for INSERT, UPDATE, and DELETE statements. Laravel makes this a breeze, and the proper connections will always be used whether you are using raw queries, the query builder, or the Eloquent ORM. + +To see how read / write connections should be configured, let's look at this example: + + 'mysql' => array( + 'read' => array( + 'host' => '192.168.1.1', + ), + 'write' => array( + 'host' => '196.168.1.2' + ), + 'driver' => 'mysql', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ), + +Note that two keys have been added to the configuration array: `read` and `write`. Both of these keys have array values containing a single key: `host`. The rest of the database options for the `read` and `write` connections will be merged from the main `mysql` array. So, we only need to place items in the `read` and `write` arrays if we wish to override the values in the main array. So, in this case, `192.168.1.1` will be used as the "read" connection, while `192.168.1.2` will be used as the "write" connection. The database credentials, prefix, character set, and all other options in the main `mysql` array will be shared across both connections. + + +## 执行crud语句 + +完成数据库配置后, 就可以直接使用DB类执行sql语句了. + +#### 执行 select 语句 + + $results = DB::select('select * from users where id = ?', array(1)); + +`select` 方法总是返回一个包含查询结果的 `array`。 + +#### 执行 Insert 语句 + + DB::insert('insert into users (id, name) values (?, ?)', array(1, 'Dayle')); + +#### 执行 Update 语句 + + DB::update('update users set votes = 100 where name = ?', array('John')); + +#### 执行 Delete 语句 + + DB::delete('delete from users'); + +> **注意:** `update` 和 `delete` 语句返回操作所影响的数据的行数(int)。 + +#### 执行非crud语句 + + DB::statement('drop table users'); + +#### 监听数据库操作事件 + +可以使用 `DB::listen` 方法监听数据库操作: + + DB::listen(function($sql, $bindings, $time) + { + // + }); + + +## 事务 + +将需要在事务模式下执行的查询放入 `transaction` 方法内即可: + + DB::transaction(function() + { + DB::table('users')->update(array('votes' => 1)); + + DB::table('posts')->delete(); + }); + +> **Note:** Any exception thrown within the `transaction` closure will cause the transaction to be rolled back automatically. + +Sometimes you may need to begin a transaction yourself: + + DB::beginTransaction(); + +You can rollback a transaction via the `rollback` method: + + DB::rollback(); + +Lastly, you can commit a transaction via the `commit` method: + + DB::commit(); + + +## 同时使用多个数据库系统 + +有时可能需要同时使用多个数据库系统(MySQL,Postgres,SQLite,SQL Server), 通过DB类的 `DB::connection` 方法来切换: + + $users = DB::connection('foo')->select(...); + +你可能需要在数据库系统的层面上操作数据库,使用PDO实例即可: + + $pdo = DB::connection()->getPdo(); + +使用reconnect方法重新连接一个指定的数据库系统: + DB::reconnect('foo'); + +If you need to disconnect from the given database due to exceeding the underyling PDO instance's `max_connections` limit, use the `disconnect` method: + + DB::disconnect('foo'); + + +## 查询日志 + +Laravel默认会为当前请求执行的的所有查询生成日志并保存在内存中。 因此, 在某些特殊的情况下, 比如一次性向数据库中插入大量数据, 就可能导致内存不足。 在这种情况下,你可以通过 `disableQueryLog` 方法来关闭查询日志: + + DB::connection()->disableQueryLog(); + +调用 `getQueryLog` 方法可以同时获取多个查询执行后的日志: + + $queries = DB::getQueryLog(); diff --git a/cn/documentation.md b/cn/documentation.md index f693146..b7aef35 100644 --- a/cn/documentation.md +++ b/cn/documentation.md @@ -24,7 +24,7 @@ - [有用的工具函数](/docs/helpers) - [IoC 容器](/docs/ioc) - [本地化](/docs/localization) - - [Mail](/docs/mail) + - [邮件](/docs/mail) - [扩展包开发](/docs/packages) - [分页](/docs/pagination) - [队列](/docs/queues) diff --git a/cn/eloquent.md b/cn/eloquent.md new file mode 100644 index 0000000..d8ebc85 --- /dev/null +++ b/cn/eloquent.md @@ -0,0 +1,1199 @@ +# Eloquent ORM + +- [简介](#introduction) +- [基本用法](#basic-usage) +- [集体赋值](#mass-assignment) +- [插入、更新、删除](#insert-update-delete) +- [软删除](#soft-deleting) +- [时间戳](#timestamps) +- [查询范围](#query-scopes) +- [关系](#relationships) +- [查询关系](#querying-relations) +- [预先加载](#eager-loading) +- [插入相关模型](#inserting-related-models) +- [触发父模型时间戳](#touching-parent-timestamps) +- [与数据透视表工作](#working-with-pivot-tables) +- [集合](#collections) +- [访问器和调整器](#accessors-and-mutators) +- [日期调整器](#date-mutators) +- [模型事件](#model-events) +- [模型观察者](#model-observers) +- [转为数组或JSON](#converting-to-arrays-or-json) + + +## 简介 + +Laravel 自带的 Eloquent ORM 为您的数据库提供了一个优雅的、简单的 ActiveRecord 实现。每一个数据库的表有一个对应的 "Model" 用来与这张表交互。 + +在开始之前,确认已在 `app/config/database.php` 文件中配置好数据库连接。 + + +## 基本用法 + +首先,创建一个 Eloquent 模型。模型通常在 `app/models` 目录,但是您可以自由地把它们放在任何地方,只要它能根据您的 `composer.json` 文件自动加载。 + +#### 定义一个 Eloquent 模型 + + class User extends Eloquent {} + +注意我们并没有告诉 Eloquent 我们为 `User` 模型使用了哪一张表。类名的小写、复数的形式将作为表名,除非它被显式地指定。所以,在这种情况下,Eloquent 将假设 `User` 模型在 `users` 表中保存记录。您可以在模型中定义一个 `table` 属性来指定一个自定义的表名: + + class User extends Eloquent { + + protected $table = 'my_users'; + + } + +> **注意:** Eloquent 将假设每张表有一个名为 `id` 的主键。您可以定义 `primaryKey` 属性来覆盖这个约定。同样,您可以定义一个 `connection` 属性来覆盖在使用这个模型时所用的数据库连接。 + +一旦模型被定义,您可以开始在表中检索和创建记录。注意在默认情况下您将需要在表中定义 `updated_at` 和 `created_at` 字段。如果您不希望这些列被自动维护,在模型中设置 `$timestamps` 属性为 `false`。 + +#### 获取所有记录 + + $users = User::all(); + +#### 根据主键获取一条记录 + + $user = User::find(1); + + var_dump($user->name); + +> **注意:** 所有在 [查询构建器] 中适用的函数在 Eloquent 模型的查询中同样适用。 + +#### 根据主键获取一条记录或者抛出一个异常 + +有时您可能希望当记录没有被找到时抛出一个异常,允许您使用 `App::error` 处理器捕捉这些异常并显示404页面。 + + $model = User::findOrFail(1); + + $model = User::where('votes', '>', 100)->firstOrFail(); + +注册错误处理器,请监听 `ModelNotFoundException`: + + use Illuminate\Database\Eloquent\ModelNotFoundException; + + App::error(function(ModelNotFoundException $e) + { + return Response::make('Not Found', 404); + }); + +#### 使用 Eloquent 模型查询 + + $users = User::where('votes', '>', 100)->take(10)->get(); + + foreach ($users as $user) + { + var_dump($user->name); + } + +#### Eloquent 统计 + +当然,您也可以使用查询构建器的统计函数。 + + $count = User::where('votes', '>', 100)->count(); + +如果您无法通过通过连贯的接口产生查询,可以使用 `whereRaw`: + + $users = User::whereRaw('age > ? and votes = 100', array(25))->get(); + +#### Chunking Results + +If you need to process a lot (thousands) of Eloquent records, using the `chunk` command will allow you to do without eating all of your RAM: + + User::chunk(200, function($users) + { + foreach ($users as $user) + { + // + } + }); + +The first argument passed to the method is the number of records you wish to receive per "chunk". The Closure passed as the second argument will be called for each chunk that is pulled from the database. + +#### 指定查询的数据库连接 + +您可能需要在运行一个 Eloquent 查询的时候指定数据库连接,只需要使用 `on` 函数: + + $user = User::on('connection-name')->find(1); + + +## 集体赋值 + +当创建一个新的模型,您可以传递属性的数组到模型的构造函数。这些属性将通过集体赋值分配给模型。这是很方便的,但把用户的输入盲目地传给模型可能是一个**严重的**安全问题。如果把用户输入盲目地传递给模型,用户可以自由地修改**任何**或者**全部**模型的属性。基于这个原因,默认情况下所有 Eloquent 模型将防止集体赋值。 + +首先,在模型中设置 `fillable` 或 `guarded` 属性。 + +#### 在模型中定义 Fillable 属性 + +`fillable` 属性指定哪些属性可以被集体赋值。这可以在类或接口层设置。 + + class User extends Eloquent { + + protected $fillable = array('first_name', 'last_name', 'email'); + + } + +在这个例子中,只有三个被列出的属性可以被集体赋值。 + +#### 在模型中定义 Guarded 属性 + +`fillable` 的反义词是 `guarded`,将做为一个黑名单而不是白名单: + + class User extends Eloquent { + + protected $guarded = array('id', 'password'); + + } +> **Note:** When using `guarded`, you should still never pass `Input::get()` or any raw array of user controlled input into a `save` or `update` method, as any column that is not guarded may be updated. + +#### 阻止所有属性集体赋值 + +在这个例子中,`id` 和 `password` 属性将**不**被允许集体赋值。所有其他属性将被允许集体赋值。您可以使用 guard 方法阻止**所有**属性被集体赋值: + + protected $guarded = array('*'); + + +## 插入、更新、删除 + +为了从模型中向数据库中创建一个新的记录,简单地创建一个模型实例并调用 `save` 函数。 + +#### 保存一个新的模型 + + $user = new User; + + $user->name = 'John'; + + $user->save(); + +> **注意:** 通常您的 Eloquent 模型将有自动递增的键。然而,如果您希望指定您自定义的键,在模型中设置 `incrementing` 属性为 `false`。 + +您也可以使用 `create` 函数在一行代码中保存一个新的模型。被插入的模型实例将从函数中返回。但是,在您这样做之前,您需要在模型中指定 `fillable` 或者 `guarded` 属性,因为所有 Eloquent 模型默认阻止集体赋值。 + +在保存或创建一个使用自增ID的新模型之后,可以通过对象的 `id` 属性获取此自增ID: + + $insertedId = $user->id; + +#### 在模型中设置 Guarded 属性 + + class User extends Eloquent { + + protected $guarded = array('id', 'account_id'); + + } + +#### 使用模型的 Create 函数 + + // Create a new user in the database... + $user = User::create(array('name' => 'John')); + + // Retrieve the user by the attributes, or create it if it doesn't exist... + $user = User::firstOrCreate(array('name' => 'John')); + + // Retrieve the user by the attributes, or instantiate a new instance... + $user = User::firstOrNew(array('name' => 'John')); + +#### 更新一个检索到的模型 + +为了更新一个模型,您可以检索它,改变一个属性,然后使用 `save` 函数: + + $user = User::find(1); + + $user->email = 'john@foo.com'; + + $user->save(); + +#### 保存一个模型和关系 + +有时您可能希望不仅保存模型,还有它的所有关系。为此,您可以使用 `push` 函数: + + $user->push(); + +您也可以在一组模型上运行更新: + + $affectedRows = User::where('votes', '>', 100)->update(array('status' => 2)); + +> **Note:** No model events are fired when updating a set of models via the Eloquent query builder. + +#### 删除一个存在的模型 + +删除一个模型,在实例中调用 `delete` 函数: + + $user = User::find(1); + + $user->delete(); + +#### 根据主键删除一个模型 + + User::destroy(1); + + User::destroy(array(1, 2, 3)); + + User::destroy(1, 2, 3); + +当然,您可以在一组模型中运行删除查询: + + $affectedRows = User::where('votes', '>', 100)->delete(); + +#### 只更新模型的时间戳 + +如果您希望简单的在一个模型中更新时间戳,可以使用 `touch` 函数: + + $user->touch(); + + +## 软删除 + +当软删除一个模型,它并没有真的从数据库中删除。相反,一个 `deleted_at` 时间戳在记录中被设置。为一个模型开启软删除,把 `SoftDeletingTrait`应用到模型上: + + use Illuminate\Database\Eloquent\SoftDeletingTrait; + + class User extends Eloquent { + + use SoftDeletingTrait; + + protected $dates = ['deleted_at']; + + } + +为了在您的表中添加一个 `deleted_at` 字段,您可以在迁移中使用 `softDeletes` 函数: + + $table->softDeletes(); + +现在,当您在一个模型中调用 `delete` 函数,`deleted_at` 字段将被设置为当前的时间戳。在使用软删除的模型中查询,被软删除的模型将不被包含进查询结果中。为了强制已删除的模型出现在结果集中,在查询中使用 `withTrashed` 函数: + +#### 强制软删除的模型到结果集中 + +To force soft deleted models to appear in a result set, use the `withTrashed` method on the query: + + $users = User::withTrashed()->where('account_id', 1)->get(); + +The `withTrashed` method may be used on a defined relationship: + + $user->posts()->withTrashed()->get(); + +如果您希望在结果集中**只**包含软删除的模型,您可以使用 `onlyTrashed` 函数: + + $users = User::onlyTrashed()->where('account_id', 1)->get(); + +像使用“withTrashed”一样,恢复一个已被软删除的记录,使用 `restore` 函数: + + $user->restore(); + +您也可以在查询中使用 `restore` 函数: + + User::withTrashed()->where('account_id', 1)->restore(); + +`restore` 函数也可以在关系中被使用: + + $user->posts()->restore(); + +如果您希望从数据库中真正删除一个模型,您可以使用 `forceDelete` 函数: + + $user->forceDelete(); + +`forceDelete` 函数也可以在关系中被使用: + + $user->posts()->forceDelete(); + +检测一个给定的模型实例是否被软删除,可以使用 `trashed` 函数: + + if ($user->trashed()) + { + // + } + + +## 时间戳 + +默认情况下,Eloquent 在数据的表中自动地将维护 `created_at` 和 `updated_at` 字段。只需简单的添加这些 `timestamp` 字段到表中,Eloquent 将为您做剩余的工作。如果您不希望 Eloquent 维护这些字段,在模型中添加以下属性: + + class User extends Eloquent { + + protected $table = 'users'; + + public $timestamps = false; + + } + +#### 提供一个定制的时间戳格式 + +如果您希望定制时间戳的格式,可以在模型中重写 `getDateFormat` 函数: + + class User extends Eloquent { + + protected function getDateFormat() + { + return 'U'; + } + + } + + +## 查询范围 + +范围允许您容易在模型中重用查询逻辑。定义一个范围,简单的用 `scope` 为模型添加前缀: + +#### 定义一个查询范围 + + class User extends Eloquent { + + public function scopePopular($query) + { + return $query->where('votes', '>', 100); + } + + public function scopeWomen($query) + { + return $query->whereGender('W'); + } + + } + +#### 使用一个查询范围 + + $users = User::popular()->women()->orderBy('created_at')->get(); + +#### 动态范围 + +有时您可能希望定义一个接受参数的范围。只需要添加您的参数到您的范围函数: + + class User extends Eloquent { + + public function scopeOfType($query, $type) + { + return $query->whereType($type); + } + + } + +然后在范围函数调用中传递参数: + + $users = User::ofType('member')->get(); + + +## 关系 + +当然,您的数据库可能是彼此相关的。比如,一篇博客文章可能有许多评论,或者一个订单与下订单的用户相关。Eloquent 使得管理和处理这些关系变得简单。Laravel 提供了多种类型的关系: +- [一对一](#one-to-one) +- [一对多](#one-to-many) +- [多对多](#many-to-many) +- [Has Many Through](#has-many-through) +- [多态关系](#polymorphic-relations) +- [Many To Many Polymorphic Relations](#many-to-many-polymorphic-relations) + + +### 一对一 + +#### 定义一个一对一关系 + +一个一对一关系是一种非常基础的关系。比如,一个 `User` 模型可以有一个 Phone`。我们可以在 Eloquent 中定义这个关系: + + class User extends Eloquent { + + public function phone() + { + return $this->hasOne('Phone'); + } + + } + +传递给 `hasOne` 函数的第一个参数是相关模型的名字。一旦这个关系被定义,我们可以使用 Eloquent 的 [动态属性] 获取它: + + $phone = User::find(1)->phone; + +这条语句所产生的 SQL 语句如下: + + select * from users where id = 1 + + select * from phones where user_id = 1 + +注意 Eloquent 假设关系的外键基于模型的名字。在这个例子中假设 `Phone` 模型使用一个 `user_id` 外键。如果您希望覆盖这个惯例,您可以为传递 `hasOne` 函数传递第二个参数。Furthermore, you may pass a third argument to the method to specify which local column that should be used for the association: + + return $this->hasOne('Phone', 'foreign_key'); + + return $this->hasOne('Phone', 'foreign_key', 'local_key'); + +#### 定义一个逆向关系 + +在 `Phone` 模型定义逆向关系,我们使用 `belongsTo` 函数: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongsTo('User'); + } + + } + +在上面的例子中,Eloquent 将在 `phones` 表中寻找 `user_id` 字段。如果您想定义一个不同的外键字段,您可以通过 `belongsTo` 函数的第二个参数传递它: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongsTo('User', 'local_key'); + } + + } + +Additionally, you pass a third parameter which specifies the name of the associated column on the parent table: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongsTo('User', 'local_key', 'parent_key'); + } + + } + + +### 一对多 + +一个一对多关系的例子是一篇博客文章有许多评论。我们可以像这样定义关系模型: + + class Post extends Eloquent { + + public function comments() + { + return $this->hasMany('Comment'); + } + + } + +现在我们可以通过 [动态属性](#dynamic-properties) 访问文章的评论: + + $comments = Post::find(1)->comments; + +如果您需要添加进一步的约束检索哪些评论,我们可以调用 `comments` 函数连接链式条件: + + $comments = Post::find(1)->comments()->where('title', '=', 'foo')->first(); + +再次,如果您想覆盖默认的外键,可以给 `hasMany` 函数传递第二个参数。And, like the `hasOne` relation, the local column may also be specified: + + return $this->hasMany('Comment', 'foreign_key'); + + return $this->hasMany('Comment', 'foreign_key', 'local_key'); + +#### 定义逆向关系 + +在 `Comment` 模型中定义逆向关系,我们使用 `belongsTo` 函数: + + class Comment extends Eloquent { + + public function post() + { + return $this->belongsTo('Post'); + } + + } + + +### 多对多 + +多对多关系更复杂的关系类型。这种关系的一个例子是一个用户和角色,这个角色也可被其他用户共享。比如,许多用户有 "Admin" 的角色。这个关系需要三个表:`users`, `roles` 以及 `role_user`。`role_user` 表来源于相关的角色名,并且有 `user_id` 和 `role_id` 字段。 + +我们可以使用 `belongsToMany` 函数定义一个多对多关系: + + class User extends Eloquent { + + public function roles() + { + return $this->belongsToMany('Role'); + } + + } + +现在,我们可以通过 `User` 模型获取角色: + + $roles = User::find(1)->roles; + +如果您想使用一个非常规的表名作为关系表,您可以传递它作为第二个参数给 `belongsToMany` 函数: + + return $this->belongsToMany('Role', 'user_roles'); + +您也可以覆盖相关的键: + + return $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id'); + +当然,您也可以定义在 `Role` 模型中定义逆向关系: + + class Role extends Eloquent { + + public function users() + { + return $this->belongsToMany('User'); + } + + } + + +### Has Many Through + +The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a `Country` model might have many `Posts` through a `Users` model. The tables for this relationship would look like this: + + countries + id - integer + name - string + + users + id - integer + country_id - integer + name - string + + posts + id - integer + user_id - integer + title - string + +Even though the `posts` table does not contain a `country_id` column, the `hasManyThrough` relation will allow us to access a country's posts via `$country->posts`. Let's define the relationship: + + class Country extends Eloquent { + + public function posts() + { + return $this->hasManyThrough('Post', 'User'); + } + + } + +If you would like to manually specify the keys of the relationship, you may pass them as the third and fourth arguments to the method: + + class Country extends Eloquent { + + public function posts() + { + return $this->hasManyThrough('Post', 'User', 'country_id', 'user_id'); + } + + } + + +### 多态关系 + +多态关系允许在一个关联中一个模型属于多于一个的其他模型。比如,您可能有一个 photo 模型属于一个员工模型或者一个订单模型。我们可以定义像这样定义这个关系: + + class Photo extends Eloquent { + + public function imageable() + { + return $this->morphTo(); + } + + } + + class Staff extends Eloquent { + + public function photos() + { + return $this->morphMany('Photo', 'imageable'); + } + + } + + class Order extends Eloquent { + + public function photos() + { + return $this->morphMany('Photo', 'imageable'); + } + + } + +#### 获取一个多态关系 + +现在,我们可以为一个员工或者一个订单获取照片: + + $staff = Staff::find(1); + + foreach ($staff->photos as $photo) + { + // + } + +#### 获取多态关系的属主 + +然而,"polymorphic" 的真正魔力是当您从 `Photo` 模型中访问员工或者订单的时候: + + $photo = Photo::find(1); + + $imageable = $photo->imageable; + +`Photo` 模型的 `imageable` 关系将返回一个 `Staff` 或者 `Order` 实例,依赖于那种类型的模型拥有这个照片。 + +#### 多态关系的数据库结构 + +帮助理解这是怎样工作的,让我们探讨一个多态关系的数据库结构: + + staff + id - integer + name - string + + orders + id - integer + price - integer + + photos + id - integer + path - string + imageable_id - integer + imageable_type - string + +这里需要注意的键字段是表 `photos` 的 `imageable_id` 和 `imageable_type` 字段。ID 将包含所属员工或订单的 ID,类型将包含所属模型的类名。当访问 `imageable` 关系的时候将允许 ORM 检测所属模型的类型。 + + +### Many To Many Polymorphic Relations + +#### Polymorphic Many To Many Relation Table Structure + +In addition to traditional polymorphic relations, you may also specify many-to-many polymorphic relations. For example, a blog `Post` and `Video` model could share a polymorphic relation to a `Tag` model. First, let's examine the table structure: + + posts + id - integer + name - string + + videos + id - integer + name - string + + tags + id - integer + name - string + + taggables + tag_id - integer + taggable_id - integer + taggable_type - string + +Next, we're ready to setup the relationships on the model. The `Post` and `Video` model will both have a `morphToMany` relationship via a `tags` method: + + class Post extends Eloquent { + + public function tags() + { + return $this->morphToMany('Tag', 'taggable'); + } + + } + +The `Tag` model may define a method for each of its relationships: + + class Tag extends Eloquent { + + public function posts() + { + return $this->morphedByMany('Post', 'taggable'); + } + + public function videos() + { + return $this->morphedByMany('Video', 'taggable'); + } + + } + + +## 查询关系 + +#### Querying Relations When Selecting + +当为一个模型获取记录的时候,您可能希望基于一个已存在的关系限制结果集的数目。比如,您希望获取至少有一条评论的文章。为此,您可以使用 `has` 函数: + + $posts = Post::has('comments')->get(); + +您也可以指定一个操作符和数量: + + $posts = Post::has('comments', '>=', 3)->get(); + +If you need even more power, you may use the `whereHas` and `orWhereHas` methods to put "where" conditions on your `has` queries: + + $posts = Post::whereHas('comments', function($q) + { + $q->where('content', 'like', 'foo%'); + + })->get(); + + +### 动态属性 + +Eloquent 允许您通过动态属性访问关系。Eloquent 将为您自动加载关系,并且足够聪明到知道是否调用 `get` (为一对多关系)或者 `first` (为一对一关系)。然后,它将通过动态属性访问作为关系。比如,使用下面的 `Phone` 模型: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongsTo('User'); + } + + } + + $phone = Phone::find(1); + +不需要像这样打印用户的电子邮件: + + echo $phone->user()->first()->email; + +它可以简化为: + + echo $phone->user->email; + +> **注意:** 返回多个结果的关系,其本质是返回了 `Illuminate\Database\Eloquent\Collection` 类的一个实例。 + + +## 预先加载 + +预先加载的存在是为了缓解 N + 1 查询问题。比如,考虑一个 `Author` 相关的 `Book` 模型。关系定义是这样的: + + class Book extends Eloquent { + + public function author() + { + return $this->belongsTo('Author'); + } + + } + +现在,考虑下面的代码: + + foreach (Book::all() as $book) + { + echo $book->author->name; + } + +此循环将执行一个查询获取表中的所有书籍,然后另一个查询为每本书获取作者。所以,如果我们有 25 本书,这个循环将允许 26 个查询。 + +值得庆幸的是,我们可以使用预先加载来减少查询的数量。这个关系应该被预先加载通过 `with` 函数指定: + + foreach (Book::with('author')->get() as $book) + { + echo $book->author->name; + } + +在上面的循环中,只有两个查询被执行: + + select * from books + + select * from authors where id in (1, 2, 3, 4, 5, ...) + +明智的使用预先加载可以大大提高应用程序的性能。 + +当然,您可以一次性预先加载多个关系: + + $books = Book::with('author', 'publisher')->get(); + +您甚至可以预先加载嵌套关系: + + $books = Book::with('author.contacts')->get(); + +在上面的例子中,`author` 关系将被预先加载,而且作者的 `contacts` 关系也将被加载。 + +### 预先加载约束 + +有时您可能希望预先加载一个关系,但也为预先加载指定一个条件。这里是一个例子: + + $users = User::with(array('posts' => function($query) + { + $query->where('title', 'like', '%first%'); + }))->get(); + +在这个例子中,我们预先加载用户的文章,但只限于文章的 title 字段中包含单词 "first" 的文章: + +Of course, eager loading Closures aren't limited to "constraints". You may also apply orders: + + $users = User::with(array('posts' => function($query) + { + $query->orderBy('created_at', 'desc'); + + }))->get(); + +### 延迟预先加载 + +从一个已存在的模型中直接预先加载相关的模型也是可能的。这在动态的决定是否加载相关模型或与缓存结合时可能有用。 + + $books = Book::all(); + + $books->load('author', 'publisher'); + + +## 插入相关模型 + +#### 附加一个相关的模型 + +您会经常需要插入新的相关模型。比如,你可能希望为一篇文章插入一条新的评论。不需要在模型中手动设置 `post_id` 外键,您可以直接从它的父模型 `Post` 中插入新的评论: + + $comment = new Comment(array('message' => 'A new comment.')); + + $post = Post::find(1); + + $comment = $post->comments()->save($comment); + +在这个例子中,所插入评论的 `post_id` 字段将自动设置。 + +### 相关模型 (属于) + +当更新一个 `belongsTo` 关系,您可以使用 `associate` 函数,这个函数将为子模型设置外键: + + $account = Account::find(10); + + $user->account()->associate($account); + + $user->save(); + +### 插入相关模型 (多对多) + +当处理多对多关系时,您也可以插入相关模型。让我们继续使用我们的 `User` 和 `Role` 模型作为例子。我们能够轻松地使用 `attach` 函数为一个用户附加新的角色: + +#### 附加多对多模型 + + $user = User::find(1); + + $user->roles()->attach(1); + +您也可以传递一个属性的数组,存在关系的表中: + + $user->roles()->attach(1, array('expires' => $expires)); + +当然,`attach` 的反义词是 `detach`: + + $user->roles()->detach(1); + +#### 使用 Sync 附加多对多关系 + +您也可以使用 `sync` 函数附加相关的模型。`sync` 函数接受一个 IDs 数组。当这个操作完成,只有数组中的 IDs 将会为这个模型存在关系表中: + + $user->roles()->sync(array(1, 2, 3)); + +#### 同步时添加关系数据 + +您也可以用给定的 IDs 关联其他关系表中的值: + + $user->roles()->sync(array(1 => array('expires' => true))); + +有时您可能希望创建一个新的相关的模型,并且在一行中附加它。对于此操作,您可以使用 `save` 函数: + + $role = new Role(array('name' => 'Editor')); + + User::find(1)->roles()->save($role); + +在这个例子中,新的 `Role` 模型将被保存并附加到用户模型。您也可以传递一个属性的数组加入到在这个操作中所连接的表: + + User::find(1)->roles()->save($role, array('expires' => $expires)); + + +## 触发父模型时间戳 + +当一个模型 `belongsTo` 另一个模型,比如一个 `Comment` 属于一个 `Post`,当更新子模型时更新父模型的时间戳通常是有用的。比如,当一个 `Comment` 模型更新,您想自动更新所属 `Post` 的 `updated_at` 时间戳。Eloquent 使之变得容易,只需要在子模型中添加 `touches` 属性包含关系的名字: + + class Comment extends Eloquent { + + protected $touches = array('post'); + + public function post() + { + return $this->belongsTo('Post'); + } + + } + +现在,当您更新一个 `Comment`,所属的 `Post` 的 `updated_at` 字段也将被更新: + + $comment = Comment::find(1); + + $comment->text = 'Edit to this comment!'; + + $comment->save(); + + +## 与数据透视表工作 + +正如您已经了解到,使用多对多关系需要一个中间表的存在。Eloquent 提供了一些非常有用与这个表交互的方式。比如,让我们假设我们的 `User` 对象有很多相关的 `Role` 对象,在访问这个关系时,我们可以在这些模型中访问数据透视表: + + $user = User::find(1); + + foreach ($user->roles as $role) + { + echo $role->pivot->created_at; + } + +注意每一个我们检索的 `Role` 模型将自动赋值给一个 `pivot` 属性。这个属性包含一个代表中间表的模型,并且可以像其他 Eloquent 模型使用。 + +默认情况下,在 `pivot` 对象中只有键,如果您的数据透视表包含其他的属性,您必须在定义关系时指定它们: + + return $this->belongsToMany('Role')->withPivot('foo', 'bar'); + +现在,`foo` 和 `bar` 属性将可以通过 `Role` 模型的 `pivot` 对象中访问。 + +如果您想您的数据透视表有自动维护的 `created_at` 和 `updated_at` 时间戳,在关系定义时使用 `withTimestamps` 方法: + + return $this->belongsToMany('Role')->withTimestamps(); + +#### 在一个数据透视表中删除记录 + +为一个模型在数据透视表中删除所有记录,您可以使用 `detach` 函数: + + User::find(1)->roles()->detach(); + +注意这个操作并不从 `roles` 删除记录,只从数据透视表中删除。 + +#### Updating A Record On A Pivot Table + +Sometimes you may need to update your pivot table, but not detach it. If you wish to update your pivot table in place you may use `updateExistingPivot` method like so: + + User::find(1)->roles()->updateExistingPivot($roleId, $attributes); + +#### Defining A Custom Pivot Model + +Laravel also allows you to define a custom Pivot model. To define a custom model, first create your own "Base" model class that extends `Eloquent`. In your other Eloquent models, extend this custom base model instead of the default `Eloquent` base. In your base model, add the following function that returns an instance of your custom Pivot model: + + public function newPivot(Model $parent, array $attributes, $table, $exists) + { + return new YourCustomPivot($parent, $attributes, $table, $exists); + } + + +## 集合 + +所有通过 `get` 方法或一个`relationship`由Eloquent返回的多结果集都是一个集合对象。这个对象实现了 `IteratorAggregate` PHP 接口,所以可以像数组一样进行遍历。然而,这个对象也有很多其他的函数来处理结果集。 + +#### 检测一个集合是否包含一个键 + +比如,我们可以使用 `contains` 检测一个结果集是否包含指定的主键: + + $roles = User::find(1)->roles; + + if ($roles->contains(2)) + { + // + } + +集合也可以被转换为一个数组或JSON: + + $roles = User::find(1)->roles->toArray(); + + $roles = User::find(1)->roles->toJson(); + +如果一个集合被转换为一个字符转,它将以JSON格式返回: + + $roles = (string) User::find(1)->roles; + +#### 遍历集合 + +Eloquent 集合也包含一些方法来遍历和过滤所包含的项: + + $roles = $user->roles->each(function($role) + { + // + }); + +#### 过滤集合 + +过滤集合时,你所提供的回调函数将被作为 [array_filter](http://php.net/manual/en/function.array-filter.php) 的回调函数使用。 + + $users = $users->filter(function($user) + { + return $user->isAdmin(); + }); + +> **注意:** 当过滤集合并转化为JSON格式时,应首先调用 `values` 函数重置数组内所有的键(key)。 + +#### 对每个集合项应用回调函数 + + $roles = User::find(1)->roles; + + $roles->each(function($role) + { + // + }); + +#### 根据一个值排序集合 + + $roles = $roles->sortBy(function($role) + { + return $role->created_at; + }); + +#### Sorting A Collection By A Value + + $roles = $roles->sortBy('created_at'); + +#### Returning A Custom Collection Type + +有时,您可能希望返回一个自定义的集合以及自己添加的函数。您可以通过在 Eloquent 模型中重写 `newCollection` 函数指定这些: + + class User extends Eloquent { + + public function newCollection(array $models = array()) + { + return new CustomCollection($models); + } + + } + + +## 访问器和调整器 + +#### 定义一个访问器 + +Eloquent 为获取和设置模型的属性提供了一种便利的方式。简单的在模型中定义一个 `getFooAttribute` 函数声明一个访问器。记住,这个函数应该遵循 "camel-casing" 拼写格式,即使您的数据库使用 "snake-case": + + class User extends Eloquent { + + public function getFirstNameAttribute($value) + { + return ucfirst($value); + } + + } + +在上面的例子中,`first_name` 字段有一个访问器。注意属性的值被传递到访问器。 + +#### 定义一个调整器 + +调整器以类似的方式声明: + + class User extends Eloquent { + + public function setFirstNameAttribute($value) + { + $this->attributes['first_name'] = strtolower($value); + } + + } + + +## 日期调整器 + +默认情况下,Eloquent 将转换 `created_at`、`updated_at` 以及 `deleted_at` 字段为 [Carbon](https://github.com/briannesbitt/Carbon) 的实例,它提供了各种有用的函数,并且继承自原生 PHP 的 `DateTime` 类。 + +您可以指定哪些字段自动调整,设置禁用调整,通过在模型中重写 `getDates` 函数: + + public function getDates() + { + return array('created_at'); + } + +当一个字段被认为是一个日期,您可以设置它的值为一个 UNIX 时间戳,日期字符串 (`Y-m-d`),日期时间字符串,当然也可以是一个 `DateTime` 或 `Carbon` 实例。 + +完全禁用日期调整,从 `getDates` 函数中返回一个空数组: + + public function getDates() + { + return array(); + } + + +## 模型事件 + +Eloquent 模型触发一些事件,允许您使用以下函数在模型的生命周期内的许多地方插入钩子:`creating`、`created`、`updating`、 `updated`、`saving`、`saved`、`deleting`、`deleted` 、`restoring`、`restored`。 + +每当一个新的项目第一次被保存,`creating` 和 `created` 事件将被触发。如果一个项目不是新的并且 `save` 函数被调用, `updating`/`updated` 事件将被触发。在这两种情况下,`saving`/`saved` 事件都将被触发。 + +#### 通过事件取消保存操作 + +如果从 `creating`, `updating` 或者 `saving` 返回 `false` ,该操作将被取消: + + User::creating(function($user) + { + if ( ! $user->isValid()) return false; + }); + +#### 设置一个模型 Boot 函数 + +Eloquent 模型也包含一个静态的 `boot` 函数,它可以提供一个方便的地方注册事件绑定。 + + class User extends Eloquent { + + public static function boot() + { + parent::boot(); + + // Setup event bindings... + } + + } + + +## 模型观察者 + +为加强处理模型事件,您可以注册一个模型观察者。一个观察者类可以包含很多函数对应于很多模型事件。比如,`creating`, `updating`, `saving` 函数可以在一个观察者中,除其他模型事件名之外。 + +因此,比如,一个模型观察者可以像这样: + + class UserObserver { + + public function saving($model) + { + // + } + + public function saved($model) + { + // + } + + } + +您可以使用 `observe` 函数注册一个观察者实例: + + User::observe(new UserObserver); + + +## 转为数组或JSON + +#### 转换一个模型为数组 + +当构建JSON APIs,您可能经常需要转换您的模型和关系为数组或JSON字符串。所以,Eloquent 包含这些方法。转换一个模型和它加载的关系为一个数组,您可以使用 `toArray` 函数: + + $user = User::with('roles')->first(); + + return $user->toArray(); + +注意模型的全部集合也可以被转为数组: + + return User::all()->toArray(); + +#### 转换一个模型为JSON字符串 + +转换一个模型为JSON字符串,您可以使用 `toJson` 函数: + + return User::find(1)->toJson(); + +#### 从路由中返回一个模型 + +注意当一个模型或集合转换为一个字符串,它将转换为JSON,这意味着您可以直接从应用程序的路由中返回 Eloquent 对象! + + Route::get('users', function() + { + return User::all(); + }); + +#### 从数组或JSON转换中隐藏属性 + +有时您可能希望限制包含在模型数组或JSON中的属性,比如密码,为此,在模型中添加一个隐藏属性: + + class User extends Eloquent { + + protected $hidden = array('password'); + + } + +> **注意:** 如果关系被隐藏了,请使用关系的 **method** 名称,而不是动态的访问名称。 + +或者,您可以使用 `visible` 属性定义一个白名单: + + protected $visible = array('first_name', 'last_name'); + + +某些时候,你可能需要向数据库添加一组属性,而这组属性并在数据库中没有对应的字段。为了能够实现这一需求,可以简单的为每个属性定义一个访问器: + + public function getIsAdminAttribute() + { + return $this->attributes['admin'] == 'yes'; + } + +一旦创建了访问器,只需将属性添加到模型的`appends`属性中: + + protected $appends = array('is_admin'); + +一旦将属性添加到`appends`列表中,它就将被包含在模型和JSON表单中。 diff --git a/cn/errors.md b/cn/errors.md new file mode 100644 index 0000000..56ed038 --- /dev/null +++ b/cn/errors.md @@ -0,0 +1,119 @@ +# 错误 & 日志 + +- [配置](#configuration) +- [处理错误](#handling-errors) +- [HTTP异常](#http-exceptions) +- [处理404错误](#handling-404-errors) +- [日志](#logging) + + +## Configuration + +The logging handler for your application is registered in the `app/start/global.php` [start file](/docs/lifecycle#start-files). By default, the logger is configured to use a single log file; however, you may customize this behavior as needed. Since Laravel uses the popular [Monolog](https://github.com/Seldaek/monolog) logging library, you can take advantage of the variety of handlers that Monolog offers. + +For example, if you wish to use daily log files instead of a single, large file, you can make the following change to your start file: + + $logFile = 'laravel.log'; + + Log::useDailyFiles(storage_path().'/logs/'.$logFile); + + +### 错误详情 + +默认情况下,应用中会默认输出错误详情。这就是说当有错误发生时,你将看到一个详细的堆栈轨迹(stack trace)和错误信息页面。你可以在`app/config/app.php`文件中将`debug`配置选项设为`false`来关闭它。 + +> **注意:** 强烈建议在生产环境中关闭错误详情。 + + +## 处理错误 + +默认情况下,`app/start/global.php`文件中包含了一个用于处理所有异常的处理器: + + App::error(function(Exception $exception) + { + Log::error($exception); + }); + +这是一个最基本错误处理器。当然,你也可以自己定义更多的错误处理器。框架会根据你在定义错误处理器时指定的异常类型(type-hint)来分别调用。例如,你定义一个仅处理`RuntimeException`异常的处理器: + + App::error(function(RuntimeException $exception) + { + // Handle the exception... + }); + +如果异常处理器返回一个response,此response将会发送给浏览器,其他异常处理器将不会再被调用: + + App::error(function(InvalidUserException $exception) + { + Log::error($exception); + + return 'Sorry! Something is wrong with this account!'; + }); + +若要监听PHP中的致命错误(fatal error),可以使用`App::fatal`方法: + + App::fatal(function($exception) + { + // + }); + +如果同时有多个异常处理器,应该先定义最通用的,然后定义最具体的异常处理器。例如,处理所有 `Exception` 类型的异常处理器应当在处理 `Illuminate\Encryption\DecryptException` 类型的异常处理器之前。 + +### 在什么位置定义错误处理器 + +Laravel框架没有限定在某个“默认”的位置定义错误处理器,而是交给你自由处理。比如可以选择在 `start/global.php` 文件中定义错误处理器。这个文件通常用于存放“启动”过程的代码。如果这个文件过于庞大,你可以创建一个 `app/errors.php` 文件,并在 `start/global.php` 脚本中 `require` 该文件。另一个可选的途径是创建一个 [服务提供器](/docs/ioc#service-providers) 来定义错误处理器。总之,对于该问题没有“正确”的答案,取决于你的偏好。 + + +## HTTP 异常 + +有些异常用于描述来自服务器的HTTP错误码,如“页面不存在” 错误(404)、“未授权错误” (401) 以及由开发者生成的500错误等。通过下面的代码可以返回这种响应: + + App::abort(404); + +你还可以为响应提供一个可选参数: + + App::abort(403, 'Unauthorized action.'); + +该方法可以在请求生命周期的任何时候调用。 + + +## 处理404错误 + +你可以注册一个错误处理器来处理应用中的所有"404 Not Found"错误,这样就可以更早的返回一个自定义的404错误页面: + + App::missing(function($exception) + { + return Response::view('errors.missing', array(), 404); + }); + + +## 日志 + +Laravel日志工具构建在强大的[Monolog](http://github.com/seldaek/monolog)库之上,并提供了一层简单的抽象层。默认情况下,Laravel为应用创建一个单独的日志文件,此文件的路径是`app/storage/logs/laravel.log`。你可以通过如下方法输出log: + + Log::info('This is some useful information.'); + + Log::warning('Something could be going wrong.'); + + Log::error('Something is really going wrong.'); + +日志工具提供了7个日志级别,参见[RFC 5424](http://tools.ietf.org/html/rfc5424):**debug**、**info**、**notice**、**warning**、**error**、**critical** 和 **alert**。 + +还可以像Log方法传递一个包含额外数据的数组: + + Log::info('Log message', array('context' => 'Other helpful information')); + +Monolog提供了多种处理日志的功能。如果需要,你可以通过如下方法获取Laravel内部所使用的Monolog实例: + + $monolog = Log::getMonolog(); + +你还可以注册一个事件,用以获取所有传输到日志的信息: + +#### 注册一个日志监听器 + + Log::listen(function($level, $message, $context) + { + // + }); + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/events.md b/cn/events.md new file mode 100644 index 0000000..75dbb7f --- /dev/null +++ b/cn/events.md @@ -0,0 +1,175 @@ +# 事件 + +- [基本用法](#basic-usage) +- [利用通配符匹配多个事件](#wildcard-listeners) +- [使用类作为监听器](#using-classes-as-listeners) +- [事件队列](#queued-events) +- [事件订阅者](#event-subscribers) + + +## 基本用法 + +Laravel中的`Event`类实现了简单的观察者模式,允许你在应用程序中订阅和监听事件。 + +#### 订阅事件 + + Event::listen('auth.login', function($user) + { + $user->last_login = new DateTime; + + $user->save(); + }); + +#### 触发事件 + + $event = Event::fire('auth.login', array($user)); + +#### 带有优先级的事件订阅 + +你也可以在订阅事件时指定优先级。优先级高的监听器将会被优先执行,相同优先级的将被按照订阅顺序执行。 + + Event::listen('auth.login', 'LoginHandler', 10); + + Event::listen('auth.login', 'OtherHandler', 5); + +#### 阻止事件传播 + +如果希望阻止事件传播到其他监听器,只需让监听器返回`false`即可实现: + + Event::listen('auth.login', function($event) + { + // Handle the event... + + return false; + }); + +### 在哪儿注册事件 + +So, you know how to register events, but you may be wondering _where_ to register them. Don't worry, this is a common question. Unfortunately, it's a hard question to answer because you can register an event almost anywhere! But, here are some tips. Again, like most other bootstrapping code, you may register events in one of your `start` files such as `app/start/global.php`. + +If your `start` files are getting too crowded, you could create a separate `app/events.php` file that is included from a `start` file. This is a simple solution that keeps your event registration cleanly separated from the rest of your bootstrapping. If you prefer a class based approach, you may register your events in a [service provider](/docs/ioc#service-providers). Since none of these approaches is inherently "correct", choose an approach you feel comfortable with based on the size of your application. + + +## 利用通配符匹配多个事件 + +#### 注册多个事件的监听器 + +注册事件监听器时,可以使用星号匹配多个事件进行监听: + + Event::listen('foo.*', function($param) + { + // Handle the event... + }); + +该监听器将会处理所有以`foo.`开始的事件。 + +You may use the `Event::firing` method to determine exactly which event was fired: + + Event::listen('foo.*', function($param) + { + if (Event::firing() == 'foo.bar') + { + // + } + }); + + +## 使用类作为监听器 + +在某些情况下,你可能希望使用类来代替闭包处理事件。类事件监听器(Class event listener)通过 [Laravel IoC 容器](/docs/ioc)实现,为监听器提供了强大的依赖注入的能力。 + +#### 注册一个类监事件听器 + + Event::listen('auth.login', 'LoginHandler'); + +#### 定义一个事件监听器类 + +默认情况下,`LoginHandler`类的`handle`方法将被调用: + + class LoginHandler { + + public function handle($data) + { + // + } + + } + +#### 指定实现订阅的方法 + +如果你不想使用默认的`handle`方法,你可以在订阅时指定方法: + + Event::listen('auth.login', 'LoginHandler@onLogin'); + + +## 事件队列 + +#### 向事件队列中添加一个事件 + +使用`queue`和`flush`方法,可以将事件放入"队列"中,而不是立刻执行: + + Event::queue('foo', array($user)); + +#### 注册一个事件清除器 + + Event::flusher('foo', function($user) + { + // + }); + +最后,你可以使用`flush`方法清除掉队列中的所有事件: + + Event::flush('foo'); + + +## 事件订阅者 + +#### 定义一个事件订阅者 + +事件订阅者类可以在其自身内部订阅多个事件。并且事件订阅者应该定义`subscribe`方法,并接收一个事件调度器实例作为参数: + + class UserEventHandler { + + /** + * Handle user login events. + */ + public function onUserLogin($event) + { + // + } + + /** + * Handle user logout events. + */ + public function onUserLogout($event) + { + // + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + * @return array + */ + public function subscribe($events) + { + $events->listen('auth.login', 'UserEventHandler@onUserLogin'); + + $events->listen('auth.logout', 'UserEventHandler@onUserLogout'); + } + + } + +#### 注册一个事件订阅者 + +一旦定义了事件订阅者,就可以使用`Event`类进行注册。 + + $subscriber = new UserEventHandler; + + Event::subscribe($subscriber); + +You may also use the [Laravel IoC container](/docs/ioc) to resolve your subscriber. To do so, simply pass the name of your subscriber to the `subscribe` method: + + Event::subscribe('UserEventHandler'); + diff --git a/cn/extending.md b/cn/extending.md new file mode 100644 index 0000000..a886554 --- /dev/null +++ b/cn/extending.md @@ -0,0 +1,235 @@ +# 对框架进行扩展 + +- [介绍](#introduction) +- [管理者 & 工厂](#managers-and-factories) +- [Where To Extend](#where-to-extend) +- [缓存](#cache) +- [Session会话](#session) +- [用户验证](#authentication) +- [基于IoC的扩展](#ioc-based-extension) +- [请求(Request)相关扩展](#request-extension) + + +## 介绍 +Laravel 为你提供了很多可扩展的地方, 通过它们你可以定制框架中一些核心组件的行为,甚至对这些核心组件进行全部替换。 例如, 哈希组件是在 "HaserInterface" 接口中被定义的,你可以根据应用程序的需求来选择是否实现它。 你也可以扩展 "Request" 对象来添加专属你的"帮助"方法。你甚至可以添加全新的 用户验证,缓存以及会话(Session) 驱动! + +Laravel 的组件通常以两种方式进行扩展:在IoC容器中绑定新的实现,或者为一个扩展注册一个 "Manager" 类,第二种方式运用了设计模式中"工厂"的理念。在这一章中,我们会探索扩展框架的各种方法以及查看一些具体的实现代码。 + +> **注意:** +请记住,Laravel 组件的扩展通常使用以下两种方法的其中一种实现的:IoC绑定和 "Manager" 类。 Manager类充当实现工厂模式的作用,它负责缓存、会话(Session)等基本驱动的实例化工作。 + + +## 管理者 & 工厂 + +Laravel 有几个 "Manager" 类 ,用来管理一些基本驱动组件的创建工作。 包括 缓存、会话(Session)、用户验证以及队列组件。"Manager"类负责根据应用程序中相关配置来创建一个驱动实现。例如,"CacheManager"类可以创建 APC、Memcached、File 以及其他各种缓存驱动的实现。 +每一个Manager类都包含有一个"extend"方法,通过它可以轻松的将新驱动中的解决方案和功能注入到Manager中。下面,我们会通过列举一些向Manager中注入定制驱动的例子来依次对它们进行说明。 + + +> **注意:** +花一些时间来探索Laravel中各种不同的"Manager"类,例如,"CacheManager"以及"SessionManager"。通过阅读这些类,可以让你对Laravel的底层实现有一个更加彻底的了解。 所有的"Manager"类都继承了"Illuminate\Support\Manager"基类, 这个基类为每一个"Manager"类提供了一些有用的,常见的功能。 + + +## Where To Extend + +This documentation covers how to extend a variety of Laravel's components, but you may be wondering where to place your extension code. Like most other bootstrapping code, you are free to place some extensions in your `start` files. Cache and Auth extensions are good candidates for this approach. Other extensions, like `Session`, must be placed in the `register` method of a service provider since they are needed very early in the request life-cycle. + + +## 缓存 + +要扩展Laravel的缓存机制,我们需要使用"CacheManager"的"extend"方法来为"manager"绑定一个定制的驱动解析器,这个方法在所有的"manager"中都是通用的。例如,注册一个新的名叫"mongo"的缓存驱动,我们需要做一下操作: + + + Cache::extend('mongo', function($app) + { + // Return Illuminate\Cache\Repository instance... + }); + +传入"extend"方法中的第一个参数是这个驱动的名字。这个会与你在"app/config/cache.php"配置文件中的"driver"选项相对应。第二个参数是一个返回"Illuminate\Cache\Repository"实例的闭包。 这闭包需要传入一个"$app"实例, 它是"Illuminate\Foundation\Application" 的一个实例,并且是一个IoC容器。 + +为了创建我们定制的缓存驱动,我们首先应该实现"Illuminate\Cache\StoreInterface"接口。 因此,我们的 MongDB 缓存的实现应该是这样的: + + class MongoStore implements Illuminate\Cache\StoreInterface { + + public function get($key) {} + public function put($key, $value, $minutes) {} + public function increment($key, $value = 1) {} + public function decrement($key, $value = 1) {} + public function forever($key, $value) {} + public function forget($key) {} + public function flush() {} + + } + +我们要用一个MongoDB连接来实现所有这些方法。一旦完成了这些实现,我们就完成了定制驱动的注册。 + + use Illuminate\Cache\Repository; + + Cache::extend('mongo', function($app) + { + return new Repository(new MongoStore); + }); + +正如你在上面的例子中所看到的,你会在创建定制缓存驱动时使用到"Illuminate\Cache\Repository"基类。通常情况下,不需要自己创建"Repository"类。 + +如果你不知道将定制的缓存驱动代码放在哪里,那么可以考虑将它们放在Packagist中,或者你可以在应用程序的主目录下创建一个"Extension"子目录。例如,如果你的应用程序叫"Snappy",你可以将你的缓存扩展放在"app/Snappy/Extensions/MongoStore.php"中。 请记住,Laravel对于应用程序的结构没有严格的限制,你可以自由地根据自己的选择来组织你的应用程序结构。 + + +> **注意:** +当你不知道将代码放在哪里时,请回想一下"service provider" 。 我们已经讨论过,利用"service provider"来组织你的框架扩展是组织代码的最好方式。 + + + +## 会话 Session + +为Laravel扩展一个定制的Session驱动跟扩展一个缓存系统一样简单。同样,我们需要用 "extend" 方法来注册定制的驱动: + + Session::extend('mongo', function($app) + { + // Return implementation of SessionHandlerInterface + }); + +### Where To Extend The Session + +Session extensions need to be registered differently than other extensions like Cache and Auth. Since sessions are started very early in the request-lifecycle, registering the extensions in a `start` file will happen be too late. Instead, a [service provider](/docs/ioc#service-providers) will be needed. You should place your session extension code in the `register` method of your service provider, and the provider should be placed **below** the default `Illuminate\Session\SessionServiceProvider` in the `providers` configuration array. + +### Writing The Session Extension + +请注意,定制的缓存驱动需要实现"SessionHandlerInterface"接口。这个接口包含在在PHP5.4+core中。如果你正在使用PHP5.3,Laravel会帮你定义这个接口,这使得你的系统可以向前兼容。我们只需要实现这个接口中的一些简单的方法。 下面是一个简化的MongoDB实现: + + class MongoHandler implements SessionHandlerInterface { + + public function open($savePath, $sessionName) {} + public function close() {} + public function read($sessionId) {} + public function write($sessionId, $data) {} + public function destroy($sessionId) {} + public function gc($lifetime) {} + + } + +上面这些方法不像在"StoreInterface"缓存接口中的方法一样让人容易理解。下面让我们快速的了解一下每一个方法的功能: + +- "open"方法通常在基于文件的Session存储系统中使用。因为Laravel自带了PHP文件存储session的驱动。因此,你不需要在这个方法中添加任何实现。事实上 PHP强制要求我们实现这个不需要添加任何代码的方法,这实在是一个糟糕的接口设计(在后面的内容中会讨论这一点)。 +-"close"方法也像"open"方法一样,通常是可以被忽略的。大多数驱动不需要这个方法。 +-"read"方法会返回一个与传入的"$sessionId"相关联的字符串形式的Session数据。将驱动中的Session数据取出时,我们不需要做任何的序列化和转码的工作。因为Laravel会帮你处理这些。 +-"write"方法是将与"$sessionId"相关联的"$data"字符串写入到一些持久化存储系统中。例如: MongoDB,Dynamo 等等。 +-"destroy"方法将与"$sessionId"相关的数据从持久化系统中移除。 +-"gc"方法会删除超过传入"$lifetime"(一个UNIX 时间戳)的所有Session数据。对于像Memcached和Redis这一类(self-expired)自动管理超期的系统,"gc"方法内不应该有任何代码。 + +实现"SessionHandlerInterface"后,我们可以将它注册到Session管理中。 + + Session::extend('mongo', function($app) + { + return new MongoHandler; + }); + +session驱动成功注册后,我们就可以使用"app/config/session.php"配置文件中的"mongo"驱动了。 + + +> **注意:** 如果你是在编写一个定制的session处理程序,请在Packagist中分享! + + +## 用户验证 + +用户验证的扩展方法和之前讨论过的缓存和session组件的扩展方法很相似。 +同样,我们会使用这个我们已经渐渐熟悉的"extend"方法。 + + Auth::extend('riak', function($app) + { + // Return implementation of Illuminate\Auth\UserProviderInterface + }); + +"UserProviderInterface"的实现仅仅是负责从如 MySQL、Riak等 持久化存储系统中将"UserInterface"实现获取出来。这两个接口使得Laravel的用户验证机制在不同的用户数据存储方法以及使用不同的类来展示的情况下都可以保持正常的功能。 + +让我们看一下"UserProviderInterface"接口: + + interface UserProviderInterface { + + public function retrieveById($identifier); + public function retrieveByCredentials(array $credentials); + public function validateCredentials(UserInterface $user, array $credentials); + + } + +"retrieveById"方法通常接收一个代表用户的数字形式的键,例如在MySQL数据库中自增的ID字段值。"UserInterface"实现会通过这个方法来比对接收和返回的ID。 + +"retrieveByCredentials"方法接收在试图登录应用程序时传入"Auth:attempt"方法的一个认证信息数组。然后,"retrieveByCredentials"方法会从底层的持久化存储系统中查询与传入身份信息相符合的用户。通常情况下,"retrieveByCredentials"方法会根据"$credentials['username']"来设定"where"条件来进行查询。 + **"retrieveByCredentials"方法不应该试图做任何密码的确认和验证操作。** + +"validateCredentials" 方法会将传入的"$user"用户和"$credentials"身份信息进行对比并验证这个用户。例如,这个方法会将"$user->getAuthPassword()"返回的字符串和经过"Hash::make"方法哈希处理的"$credentials['password']"进行对比。 + +现在,我们已经讨论过"UserProviderInterface"中的所有方法,让我们来看一看"UserInterface"接口。请记住,"provider"必须在"retrieveById"和"retrieveByCredentials"方法中返回对于这个接口的实现: + + interface UserInterface { + + public function getAuthIdentifier(); + public function getAuthPassword(); + + } + +这个接口非常简单。"getAuthIdentifier"返回这个用户的"主键",在MySQL后台中,这个主键就是自增的primary key。"getAuthPassword"方法返回这个用户进过hash加密后的密码。这个接口可以让用户验证系统在任何User类的基础上正常工作,不管你是在使用哪种ORM或存储抽象层。默认情况下,Laravel在"app/models"目录下会有一个实现了这个接口的"User"类,因此,你可以参照这个作一些实现。 + +最后,完成了"UserProviderInterface"实现后,我们可以将"Auth" 扩展注册到用户认证管理中: + + Auth::extend('riak', function($app) + { + return new RiakUserProvider($app['riak.connection']); + }); + +当你通过extend方法注册了这个驱动后,你可以将当前的用户验证驱动切换为"app/config/auth.php"配置文件中的新驱动。 + + + +## 基于IoC的扩展 + +几乎所有的service提供者包括Laravel框架都会将对象绑定在IoC容器中,你可以在"app/config/app.php"配置文件中找到关于你应用程序中的所有service提供者。如果有时间,可以浏览这些service提供者的源代码。通过浏览这些细节,你可以更深刻的了解这些service提供者在框架中添加了什么内容,以及IoC容器利用了哪些键来绑定这些service提供者。 + +例如,"HashServiceProvider"在IoC容器中绑定了一个"hash"键,这个键对应一个"Illuminate\Hashing\BcryptHasher"实例。通过重写这个IoC绑定,你可以轻松的在你的应用程序中扩展以及重写这个类。例如: + + class SnappyHashProvider extends Illuminate\Hashing\HashServiceProvider { + + public function boot() + { + App::bindShared('hash', function() + { + return new Snappy\Hashing\ScryptHasher; + }); + + parent::boot(); + } + + } + +请注意,这个类继承的是 "HashServiceProvider" 类,而不是默认的"ServiceProvider"基类。完成service提供者的扩展之后,将在配置文件"app/config/app.php"中的"HashServiceProvider"替换成经过扩展的service提供者。 + +这是为扩展绑定在容器中的核心类而通常使用的方法。基本上每一个核心类都是通过这样的方式绑定在容器中,并且可以对它们进行重写。再一次提醒,详细阅读框架中所包含的service提供者会使你更加清楚这些类绑定在容器中的什么地方,以及它们与什么Key(键值)所绑定着。这是学习Laravel究竟是如何组织的最好的方法。 + + + +## Request(请求)扩展 + +由于"Request"是框架中非常基础性的组件,而且它在Request请求周期中很早就被实例化了,因此扩展"Request"类与前面所讲的例子有一些不同。 + +首先,用上述的方法来扩展这个类: + + + +## 简介 + +Facades 提供了一个“静态”接口到 [IoC 容器](/docs/ioc) 类 。Laravel 含有很多 facades,你可能不知道你在某些地方已经使用过它们了。Laravel "facades" serve as "static proxies" to underlying classes in the IoC container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. + +有时候, 你可能想为你的应用程序或包创建自己的 facades, 所以,让我们来讨论一下如何开发和使用这些类。 + + +> **注意** 在深入 facades 之前,我们强烈建议你多了解一下 Laravel [Ioc 容器](/docs/ioc)。 + + + +## 说明 + +在 Laravel 应用程序中, facade 是提供从容器中访问对象方法的类。`Facade` 类实现了该机制。 + +你的 facade 类只需要实现一个方法: `getFacadeAccesor` 。 `getFacadeAccessor` 方法的工作是定义如何从容器中取得对象。 `Facades` 基类构建了 `__callStatic()` 魔术方法来从 facade 延迟访问取得对象。 + +So, when you make a facade call like `Cache::get`, Laravel resolves the Cache manager class out of the IoC container and calls the `get` method on the class. In technical terms, Laravel Facades are a convenient syntax for using the Laravel IoC container as a service locator. + + +## 实际用例 + +在以下示例中,执行 Laravel 缓存系统, 查看该代码,我们可能会假定 `get` 静态方法是执行在 `Cache` 类。 + +In the example below, a call is made to the Laravel cache system. By glancing at this code, one might assume that the static method `get` is being called on the `Cache` class. + + $value = Cache::get('key'); + +然后,如果我们查看 `Illuminate\Support\Facades\Cache` 类, 你会发现该类没有任何 `get` 静态方法: + + class Cache extends Facade { + + /** + * Get the registered name of the component. + * + * @return string + */ + protected static function getFacadeAccessor() { return 'cache'; } + + } + +Cache 类继承基本 `Facade` 类,并且定义了个 `getFacadeAccessor()` 方法。注意,该方法的工作是返回 IoC 绑定的名字。 + +当用户引用任何在 `Cache` facade 静态方法, Laravel 从 IoC 容器绑定中取得 `cache`,并且执行请求该对象方法(在该例子中为`get`)。 + +所以,我们 `Cache::get` 执行可以重写为: + + $value = $app->make('cache')->get('key'); + + +## 创建 Facades + +要为自己的应用程序或者包创建一个 facade 是非常简单的。你需要做三件事情: + +- 一个 IoC 绑定。 +- 一个 facade 类。 +- 一个 facade 别名配置。 + +让我们来看个例子。这里,我们定义一个 `PaymentGateway\Payment` 类。 + + namespace PaymentGateway; + + class Payment { + + public function process() + { + // + } + + } + +This class might live in your `app/models` directory, or any other directory that Composer knows how to auto-load. + +我们需要能够在 IoC 容器中取得该类。所以,让我们增加一个绑定: + + App::bind('payment', function() + { + return new \PaymentGateway\Payment; + }); + +最好注册该绑定的位置是创建一个新的名为 `PaymentServiceProvider` [服务提供器](/docs/ioc#service-providers),并且将该绑定加入到 `register` 方法。接下来你就可以配置 Laravel `app/config/app.php` 配置文件来加载该服务提供器。 + +接下来,我们就可以创建我们的 facade 类: + + use Illuminate\Support\Facades\Facade; + + class Payment extends Facade { + + protected static function getFacadeAccessor() { return 'payment'; } + + } + +最后,如果你想,我们可以为我们 facade 设置别名到 `app/config/app.php` 配置文件里的 `aliases` 数组。现在,我们能够调用 `Payment` 类实例的 `process` 方法。 + Payment::process(); + + +## 模拟 Facades + +单元测试是 facades 工作的重要体现。事实上,可测试性是 facedes 存在的主要原因。要了解更多信息,查看文档[模拟 facades](/docs/testing#mocking-facades)部分。 + + +## Facade Class Reference + +Below you will find every facade and its underlying class. This is a useful tool for quickly digging into the API documentation for a given facade root. The [IoC binding](/docs/ioc) key is also included where applicable. + +Facade | Class | IoC Binding +------------- | ------------- | ------------- +App | [Illuminate\Foundation\Application](http://laravel.com/api/4.1/Illuminate/Foundation/Application.html) | `app` +Artisan | [Illuminate\Console\Application](http://laravel.com/api/4.1/Illuminate/Console/Application.html) | `artisan` +Auth | [Illuminate\Auth\AuthManager](http://laravel.com/api/4.1/Illuminate/Auth/AuthManager.html) | `auth` +Auth (Instance) | [Illuminate\Auth\Guard](http://laravel.com/api/4.1/Illuminate/Auth/Guard.html) | +Blade | [Illuminate\View\Compilers\BladeCompiler](http://laravel.com/api/4.1/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` +Cache | [Illuminate\Cache\Repository](http://laravel.com/api/4.1/Illuminate/Cache/Repository.html) | `cache` +Config | [Illuminate\Config\Repository](http://laravel.com/api/4.1/Illuminate/Config/Repository.html) | `config` +Cookie | [Illuminate\Cookie\CookieJar](http://laravel.com/api/4.1/Illuminate/Cookie/CookieJar.html) | `cookie` +Crypt | [Illuminate\Encryption\Encrypter](http://laravel.com/api/4.1/Illuminate/Encryption/Encrypter.html) | `encrypter` +DB | [Illuminate\Database\DatabaseManager](http://laravel.com/api/4.1/Illuminate/Database/DatabaseManager.html) | `db` +DB (Instance) | [Illuminate\Database\Connection](http://laravel.com/api/4.1/Illuminate/Database/Connection.html) | +Event | [Illuminate\Events\Dispatcher](http://laravel.com/api/4.1/Illuminate/Events/Dispatcher.html) | `events` +File | [Illuminate\Filesystem\Filesystem](http://laravel.com/api/4.1/Illuminate/Filesystem/Filesystem.html) | `files` +Form | [Illuminate\Html\FormBuilder](http://laravel.com/api/4.1/Illuminate/Html/FormBuilder.html) | `form` +Hash | [Illuminate\Hashing\HasherInterface](http://laravel.com/api/4.1/Illuminate/Hashing/HasherInterface.html) | `hash` +HTML | [Illuminate\Html\HtmlBuilder](http://laravel.com/api/4.1/Illuminate/Html/HtmlBuilder.html) | `html` +Input | [Illuminate\Http\Request](http://laravel.com/api/4.1/Illuminate/Http/Request.html) | `request` +Lang | [Illuminate\Translation\Translator](http://laravel.com/api/4.1/Illuminate/Translation/Translator.html) | `translator` +Log | [Illuminate\Log\Writer](http://laravel.com/api/4.1/Illuminate/Log/Writer.html) | `log` +Mail | [Illuminate\Mail\Mailer](http://laravel.com/api/4.1/Illuminate/Mail/Mailer.html) | `mailer` +Paginator | [Illuminate\Pagination\Factory](http://laravel.com/api/4.1/Illuminate/Pagination/Factory.html) | `paginator` +Paginator (Instance) | [Illuminate\Pagination\Paginator](http://laravel.com/api/4.1/Illuminate/Pagination/Paginator.html) | +Password | [Illuminate\Auth\Reminders\PasswordBroker](http://laravel.com/api/4.1/Illuminate/Auth/Reminders/PasswordBroker.html) | `auth.reminder` +Queue | [Illuminate\Queue\QueueManager](http://laravel.com/api/4.1/Illuminate/Queue/QueueManager.html) | `queue` +Queue (Instance) | [Illuminate\Queue\QueueInterface](http://laravel.com/api/4.1/Illuminate/Queue/QueueInterface.html) | +Queue (Base Class) | [Illuminate\Queue\Queue](http://laravel.com/api/4.1/Illuminate/Queue/Queue.html) | +Redirect | [Illuminate\Routing\Redirector](http://laravel.com/api/4.1/Illuminate/Routing/Redirector.html) | `redirect` +Redis | [Illuminate\Redis\Database](http://laravel.com/api/4.1/Illuminate/Redis/Database.html) | `redis` +Request | [Illuminate\Http\Request](http://laravel.com/api/4.1/Illuminate/Http/Request.html) | `request` +Response | [Illuminate\Support\Facades\Response](http://laravel.com/api/4.1/Illuminate/Support/Facades/Response.html) | +Route | [Illuminate\Routing\Router](http://laravel.com/api/4.1/Illuminate/Routing/Router.html) | `router` +Schema | [Illuminate\Database\Schema\Blueprint](http://laravel.com/api/4.1/Illuminate/Database/Schema/Blueprint.html) | +Session | [Illuminate\Session\SessionManager](http://laravel.com/api/4.1/Illuminate/Session/SessionManager.html) | `session` +Session (Instance) | [Illuminate\Session\Store](http://laravel.com/api/4.1/Illuminate/Session/Store.html) | +SSH | [Illuminate\Remote\RemoteManager](http://laravel.com/api/4.1/Illuminate/Remote/RemoteManager.html) | `remote` +SSH (Instance) | [Illuminate\Remote\Connection](http://laravel.com/api/4.1/Illuminate/Remote/Connection.html) | +URL | [Illuminate\Routing\UrlGenerator](http://laravel.com/api/4.1/Illuminate/Routing/UrlGenerator.html) | `url` +Validator | [Illuminate\Validation\Factory](http://laravel.com/api/4.1/Illuminate/Validation/Factory.html) | `validator` +Validator (Instance) | [Illuminate\Validation\Validator](http://laravel.com/api/4.1/Illuminate/Validation/Validator.html) +View | [Illuminate\View\Factory](http://laravel.com/api/4.1/Illuminate/View/Factory.html) | `view` +View (Instance) | [Illuminate\View\View](http://laravel.com/api/4.1/Illuminate/View/View.html) | \ No newline at end of file diff --git a/cn/helpers.md b/cn/helpers.md new file mode 100644 index 0000000..48e5877 --- /dev/null +++ b/cn/helpers.md @@ -0,0 +1,412 @@ +# 助手函数 + +- [数组助手](#arrays) +- [应用路径](#paths) +- [字符串助手](#strings) +- [URLs路径](#urls) +- [杂项](#miscellaneous) + + +## 数组助手 + +### array_add + +`array_add` 函数将一个指定键的元素添加进数组,如果数组中已有该键,则不添加。 + + $array = array('foo' => 'bar'); + + $array = array_add($array, 'key', 'value'); + +### array_divide + +`array_divide` 返回两个数组,第一个包含数组里的所有键,第二个包含数组里的所有值。 + + $array = array('foo' => 'bar'); + + list($keys, $values) = array_divide($array); + +### array_dot + +`array_dot` 函数将多维数组转为一维数组,该数组不需要规则的结构。所有的键用'.'分割。 + + $array = array('foo' => array('bar' => 'baz')); + + $array = array_dot($array); + + // array('foo.bar' => 'baz'); + +### array_except + +`array_except` 函数移除指定键的元素(数组的第一维),第二个参数包含所有要移除键的数组,并返回新数组。 + + $array = array_except($array, array('keys', 'to', 'remove')); + +### array_fetch + +`array_fetch` 获取多维数组的最终值,参数为 第一维的键.第二维的键.第三维的键.... 的形式,指定的维数 数组的形式需一致,否则Laravel将抛出'Undefined index'。 + + $array = array( + array('developer' => array('name' => 'Taylor')), + array('developer' => array('name' => 'Dayle')), + ); + + $array = array_fetch($array, 'developer.name'); + +### array_first + +`array_first` 方法返回第一个 满足匿名函数(该匿名函数作为参数传入) 返回true的元素的值。 + + $array = array(100, 200, 300); + + $value = array_first($array, function($key, $value) + { + return $value >= 150; + }); + +array_first的第三个参数为该操作指定默认返回值,若匿名函数永远不可能返回true将返回默认值。 + + $value = array_first($array, $callback, $default); + +### array_last + +The `array_last` method returns the last element of an array passing a given truth test. + + $array = array(350, 400, 500, 300, 200, 100); + + $value = array_last($array, function($key, $value) + { + return $value > 350; + }); + + // 500 + +A default value may also be passed as the third parameter: + + $value = array_last($array, $callback, $default); + +### array_flatten + +`array_flatten` 获取多维数组的最终值,该数组不需要规则的结构。并丢掉键。 + + $array = array('name' => 'Joe', 'languages' => array('PHP', 'Ruby')); + + $array = array_flatten($array); + + // array('Joe', 'PHP', 'Ruby'); + +### array_forget + +`array_forget` 方法移除数组内指定的元素,通过 键.键.键 的形式来寻找指定要移除的元素 + + $array = array('names' => array('joe' => array('programmer'))); + + array_forget($array, 'names.joe'); + +### array_get + +`array_get` 方法获取数组内指定的元素,通过 键.键.键 的形式来寻找指定的元素 + + $array = array('names' => array('joe' => array('programmer'))); + + $value = array_get($array, 'names.joe'); + +> **Note:** Want something like `array_get` but for objects instead? Use `object_get`. + +### array_only + +`array_only` 方法返回数组内指定键(仅限第一维的键)的的元素,参数为将获取的数组元素键的数组。 + + $array = array('name' => 'Joe', 'age' => 27, 'votes' => 1); + + $array = array_only($array, array('name', 'votes')); + +### array_pluck + +`array_pluck` 返回数组内指定键的值,并丢掉键,只能指定一个键。 + + $array = array(array('name' => 'Taylor'), array('name' => 'Dayle')); + + $array = array_pluck($array, 'name'); + + // array('Taylor', 'Dayle'); + +### array_pull + +`array_pull` 从原数组中删除指定键的元素,并返回被删除的元素的值。 + + $array = array('name' => 'Taylor', 'age' => 27); + + $name = array_pull($array, 'name'); + +### array_set + +`array_set` 为一个指定键的元素设置值,通过 键.键.键 的形式来寻找将被设置或重新赋值的元素。 + + $array = array('names' => array('programmer' => 'Joe')); + + array_set($array, 'names.editor', 'Taylor'); + +### array_sort + +`array_sort` 函数对数组里的第一维元素进行自定义排序,并保持第一维数组键。 + + $array = array( + array('name' => 'Jill'), + array('name' => 'Barry'), + ); + + $array = array_values(array_sort($array, function($value) + { + return $value['name']; + })); + +### array_where + +Filter the array using the given Closure. + + $array = array(100, '200', 300, '400', 500); + + $array = array_where($array, function($key, $value) + { + return is_string($value); + }); + + // Array ( [1] => 200 [3] => 400 ) + +### head + +返回数组的第一个元素,内部实际调用reset方法。经常在链式调用时使用。 + + $first = head($this->returnsArray('foo')); + +### last + +返回数组的最后一个元素,内部实际调用end方法。经常在链式调用时使用。 + + $last = last($this->returnsArray('foo')); + + +## 应用路径 + +### app_path + +获取 `app` (/app[laravel4]) 目录的绝对路径。 + + $path = app_path(); + +### base_path + +获取laravel应用所在的绝对路径。 + +### public_path + +获取 `public` 目录的绝对路径。 + +### storage_path + +获取 `app/storage` 目录的绝对路径. + + +## 字符串助手 + +### camel_case + +将字符串转为 `驼峰命名法` 的格式. + + $camel = camel_case('foo_bar'); + + // fooBar + +### class_basename + +获取不带命名空间的类名。 + + $class = class_basename('Foo\Bar\Baz'); + + // Baz + +### e + +`htmlentites` 的简写,以utf8格式转换字符串。 + + $entities = e('foo'); + +### ends_with + +判断字符串是否以另一个指定的字符串结束。 + + $value = ends_with('This is my name', 'name'); + +### snake_case + +将字符串转换为下划线命名方式。 + + $snake = snake_case('fooBar'); + + // foo_bar + +### str_limit + +Limit the number of characters in a string. + + str_limit($value, $limit = 100, $end = '...') + +Example: + + $value = str_limit('The PHP framework for web artisans.', 7); + + // The PHP... + +### starts_with + +判断字符串是否以另一个指定的字符串开始。 + + $value = starts_with('This is my name', 'This'); + +### str_contains + +判断字符串是否包含另一个指定的字符串。 + + $value = str_contains('This is my name', 'my'); + +### str_finish + +以指定字符结束字符串,且保证字符串结尾有且只有一个指定的字符。 + + $string = str_finish('this/string', '/'); + + // this/string/ + +### str_is + +判断字符串是否符合指定的格式。 下面的星号是作为通配符使用。 + + $value = str_is('foo*', 'foobar'); + +### str_plural + +将字符串转为复数形式(只适合英语)。 + + $plural = str_plural('car'); + +### str_random + +生成一个指定长度的字符串。 + + $string = str_random(40); + +### str_singular + +将字符串转为单数形式(只适合英语)。 + + $singular = str_singular('cars'); + +### studly_case + +将字符串转为 `StudlyCase` 格式。 + + $value = studly_case('foo_bar'); + + // FooBar + +### trans + +翻译指定语言行(请配合本地化语言文件使用)。`Lang::get` 的别名。 + + $value = trans('validation.required'): + +### trans_choice + +根据数量条件翻译指定语言行(主要针对外语的名词复数形式)。`Lang::choice` 的别名。 + + $value = trans_choice('foo.bar', $count); + + +## URLs + +### action + +从指定的控制器的方法生成url。 + + $url = action('HomeController@getIndex', $params); + +### 路由 + +为命名路由生成URL。 + + $url = route('routeName', $params); + +### asset + +生成一个指向样式文件的url。 + + $url = asset('img/photo.jpg'); + +### link_to + +生成一个a标签的html代码,并能为该a标签指定详细信息。 + + echo link_to('foo/bar', $title, $attributes = array(), $secure = null); + +### link_to_asset + +生成一个指向样式文件的a标签。 + + echo link_to_asset('foo/bar.zip', $title, $attributes = array(), $secure = null); + +### link_to_route + +生成一个指向特定路由的a标签。 + + echo link_to_route('route.name', $title, $parameters = array(), $attributes = array()); + +### link_to_action + +生成一个指向特定控制器方法的a标签。 + + echo link_to_action('HomeController@getIndex', $title, $parameters = array(), $attributes = array()); + +### secure_asset + +生成一个指向样式文件的a标签并使用使用 HTTPS 安全链接。 + + echo secure_asset('foo/bar.zip', $title, $attributes = array()); + +### secure_url + +生成一个完全自定义的a标签并使用 HTTPS 安全链接。 + + echo secure_url('foo/bar', $parameters = array()); + +### url + +生成一个完全自定义的a标签。 + + echo url('foo/bar', $parameters = array(), $secure = null); + + +## 杂项 + +### csrf_token + +获取当前 CSRF 标记。 + + $token = csrf_token(); + +### dd + +格式化输出指定的变量,并结束脚本执行。 + + dd($value); + +### value + +如果参数为 `Closure`,value函数将返回 `Closure` 的返回值。 否则,返回参数本。 + + $value = value(function() { return 'bar'; }); + +### with + +参数为对象,直接返回该对象,在PHP 5.3.x中进行链式调用很有用。 + + $value = with(new Foo)->doWork(); diff --git a/cn/html.md b/cn/html.md new file mode 100644 index 0000000..a94e322 --- /dev/null +++ b/cn/html.md @@ -0,0 +1,199 @@ +# 表单 & HTML + +- [创建表单](#opening-a-form) +- [CSRF防御机制](#csrf-protection) +- [表单与模型绑定](#form-model-binding) +- [Label 标签](#labels) +- [单行文本、多行文本、密码和隐藏表单域](#text) +- [多选框和单选框](#checkboxes-and-radio-buttons) +- [文件上传](#file-input) +- [下拉菜单](#drop-down-lists) +- [按钮](#buttons) +- [自定义宏](#custom-macros) +- [生成URL](#generating-urls) + + +## 创建表单 + +#### 创建表单 + + {{ Form::open(array('url' => 'foo/bar')) }} + // + {{ Form::close() }} + +默认使用 `POST` 方法;当然,你也可以指定使用其他方法提交表单: + + echo Form::open(array('url' => 'foo/bar', 'method' => 'put')) + +> **注意:** 因为 HTML 表单只支持 `POST` 方法,在使用 `PUT` 和 `DELETE` 方法时框架会自动通过一个 `_method` 隐藏域来发送伪装的方法信息。 + +你也可以创建一个表单指向已定义的路由或者控制器操作: + + echo Form::open(array('route' => 'route.name')) + + echo Form::open(array('action' => 'Controller@method')) + +如果你需要在表单中上传文件,请添加一个 `files` 参数到数组中: + + echo Form::open(array('url' => 'foo/bar', 'files' => true)) + + +## CSRF防御机制 + +#### 添加CSRF令牌到表单中 + +Laravel框架提供了一种简单的方法帮助你的应用抵御CSRF(跨站请求伪造)式攻击。Laravel框架会自动在用户 session 中存放一个随机令牌(token),并且将这个令牌作为一个隐藏字段包含在表单信息中。当然,你也可以使用表单的 `token` 方法直接调用令牌字段的HTML代码: + + echo Form::token(); + //译者注,调用结果类似: + +#### 在路由中附加CSRF过滤器 + + Route::post('profile', array('before' => 'csrf', function() + { + // + })); + + +## 表单与模型绑定 + +#### 创建一个模型表单 + +你可以使用 `Form::model` 方法将模型中的内容填充到表单中去: + + echo Form::model($user, array('route' => array('user.update', $user->id))) + +当你创建一个表单元素(如文本输入字段)时,表单域的值会被自动设置成与之匹配的模型属性的值。例如表单中name属性为 `email` 的文本域的值会被设置为用户模型中 `email` 属性的值。不仅仅这样,当Session数据中有项目与表单域名称相匹配,Session值的填充优先级会高于模型的值。具体的优先级如下: + +1. Session 数据 (旧的输入数据) +2. 已传输的数据 +3. 模型属性数据 + +这可以帮助你快速建立与模型绑定的表单,而且当服务器端验证出表单错误时可以轻松回填数据! + +> **注意:** 当你使用 `Form::model` 时,别忘了在表单结尾处加上 `Form::close` ! + + +## Label 标签 + +#### 获取一个 label 标签元素 + + echo Form::label('email', 'E-Mail Address'); + //译者注,输出结果为: + +#### 设定其他 HTML 标签属性 + + echo Form::label('email', 'E-Mail Address', array('class' => 'awesome')); + //译者注,输出结果为: + +> **注意:** 当你创建了一个label标签后,name值与之相匹配的表单元素会被自动加上一个同名的ID属性。 + + +## 单行文本、多行文本、密码和隐藏表单域 + +#### 获取一个单行文本域 + + echo Form::text('username'); + //译者注,输出结果为: + +#### 设置一个默认的值 + + echo Form::text('email', 'example@gmail.com'); + //译者注,输出结果为: + +> **注意:** 调用 *hidden* 隐藏域和 *textarea* 多行文本表单域的方法和调用 *text* 单行文本域的方法是一样的。 + +#### 获取一个密码域 + + echo Form::password('password'); + //译者注,输出结果为: + + +## 多选框和单选框 + +#### 获取一个多选框和单选框 + + echo Form::checkbox('name', 'value'); + //译者注,输出结果为: + echo Form::radio('name', 'value'); + //译者注,输出结果为: + +#### 获取一个已选中的多选框和单选框 + + echo Form::checkbox('name', 'value', true); + //译者注,输出结果为: + echo Form::radio('name', 'value', true); + //译者注,输出结果为: + + +## 文件上传 + +#### 获取一个文件上传域 + + echo Form::file('image'); + //译者注,输出结果为: + +> **Note:** The form must have been opened with the `files` option set to `true`. + + +## 下拉菜单 + +#### 获取一个下拉菜单 + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small')); + //译者注,输出结果为: + +#### 获取一个已选中默认值的下拉菜单 + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S'); + //译者注,输出结果为: + +#### 获取一个分组下拉菜单 + + echo Form::select('animal', array( + 'Cats' => array('leopard' => 'Leopard'), + 'Dogs' => array('spaniel' => 'Spaniel'), + )); + //译者注,输出结果为: + +#### 生成一个包含可取值范围的下拉菜单 + + echo Form::selectRange('number', 10, 20); + +#### 生成月份名称列表 + + echo Form::selectMonth('month'); + + +## 按钮 + +#### 获取一个提交按钮 + + echo Form::submit('Click Me!'); + //译者注,输出结果为: + +> **注意:** 如果需要创建一个单独的表单元素,可以使用 *button* 方法。它的调用方式和 *submit* 方法一样。 + + +## 自定义宏 + +#### 注册一个表单宏 + +你可以轻松定义一个表单宏来帮助你创建表单。下面是使用表单宏的方法,注册一个宏名称,然后在闭包函数中定义对应的操作: + + Form::macro('myField', function() + { + return ''; + }); + +然后你就可以使用刚刚定义的宏名称调用它: + +#### 调用一个自定义表单宏 + + echo Form::myField(); + + + +##生成URL + +请参考 [helpers](/docs/helpers#urls) 文档了解更多关于生成URL的信息。 diff --git a/cn/ioc.md b/cn/ioc.md new file mode 100644 index 0000000..c25fdae --- /dev/null +++ b/cn/ioc.md @@ -0,0 +1,186 @@ +# IoC 容器 + +- [简介](#introduction) +- [基本用例](#basic-usage) +- [Where To Register Bindings](#where-to-register) +- [自动解析](#automatic-resolution) +- [实际用例](#practical-usage) +- [服务提供器](#service-providers) +- [容器事件](#container-events) + + +## 简介 + +Laravel 控制器反转容器是一个强大的工具来处理类依赖关系。依赖注入是一个不用硬代码处理类依赖关系的方法。依赖关系是在运行时注入的,允许处理依赖时具有更大的灵活性。 + +理解 Laravel IoC 容器是构建强大应用程序所必要的,也有助于 Laravel 核心本身。 + + +## 基本用例 + +#### 绑定一个类型到容器 + +IoC 容器有两种方法来解决依赖关系:通过闭包回调或者自动解析。首先,我们来探究一下闭包回调。首先,需要绑定一个“类型”到容器中: + + App::bind('foo', function($app) + { + return new FooBar; + }); + +#### 从容器中取得一个类型 + + $value = App::make('foo'); + +当执行 `App::make` 方法,闭包函数被执行并返回结果。 + +#### 绑定一个”共享“类型到容器 + +有时,你只想将绑定到容器的类型只应该取得一次,然后接下来从容器中取得的都应该是相同实例: + + App::singleton('foo', function() + { + return new FooBar; + }); + +#### 绑定一个已经存在的类型实例到容器 + +你也可以使用 `instance`方法 将一个已经存在的类型绑定到容器中: + + $foo = new Foo; + + App::instance('foo', $foo); + + +## Where To Register Bindings + +IoC bindings, like event handlers or route filters, generally fall under the title of "bootstrap code". In other words, they prepare your application to actually handle requests, and usually need to be executed before a route or controller is actually called. Like most other bootstrap code, the `start` files are always an option for registering IoC bindings. Alternatively, you could create an `app/ioc.php` (filename does not matter) file and require that file from your `start` file. + +If your application has a very large number of IoC bindings, or you simply wish to organize your IoC bindings in separate files by category, you may register your bindings in a [service provider](#service-providers). + + +## 自动解析 + +#### 取得一个类 + +IoC 容器在许多场景下足够强大来取得类而不需要任何配置。例如: + + class FooBar { + + public function __construct(Baz $baz) + { + $this->baz = $baz; + } + + } + + $fooBar = App::make('FooBar'); + +注意我们虽然没有在容器中注册FooBar类,容器仍然可以取得该类,甚至自动注入 `Baz` 依赖! + +当某个类型没有绑定到容器,将使用 PHP 的反射工具来检查类和读取构造器的类型提示。使用这些信息,容器可以自动构建类实例。 + +然而,在某些情况下,一个类可能依赖某个接口实现,而不是一个 “具体的类”。当在这种情况下,`App::bind` 方法必须通知容器注入该实现哪个接口: + +#### 绑定一个接口给实现类 + + App::bind('UserRepositoryInterface', 'DbUserRepository'); + +现在考虑以下控制器: + + class UserController extends BaseController { + + public function __construct(UserRepositoryInterface $users) + { + $this->users = $users; + } + + } + +由于我们将 `UserRepositoryInterface` 绑定了具体类,`DbUserRepository` 在该控制器创建时将自动注入到该控制器。 + + +## 实际用例 + +Laravel 提供了几个方法使用 IoC 容器增强应用程序可扩展性和可测试性。一个主要的例子是取得控制器。所有控制器都通过 IoC 容器取得,意味着可以解决控制器构造方法类型提示的依赖关系,它们将自动被注入。 + +#### 控制器类型提示依赖关系 + + class OrderController extends BaseController { + + public function __construct(OrderRepository $orders) + { + $this->orders = $orders; + } + + public function getIndex() + { + $all = $this->orders->all(); + + return View::make('orders', compact('all')); + } + + } + +在这个例子中,`OrderRepository` 将会自动注入到控制器。意味着当 [单元测试](/docs/testing) 模拟请求时,`OrderRepository` 将会绑定到容器以及注入到控制器中,允许无痛与数据库层交互。 + +#### IoC 使用的其他例子 + +[过滤器](/docs/routing#route-filters), [composers](/docs/responses#view-composers), 和 [事件处理器](/docs/events#using-classes-as-listeners) 也通过 IoC 容器来取得。当注册了它们,仅简单提供要使用的类名: + + Route::filter('foo', 'FooFilter'); + + View::composer('foo', 'FooComposer'); + + Event::listen('foo', 'FooHandler'); + + +## 服务提供器 + +服务器提供器是将一组相关 IoC 注册到单一路径的有效方法。将它们看做是一种引导组件的方法。在服务器提供器里,你可以注册自定义的验证驱动器,使用 IoC 容器注册应用程序仓库类,甚至是自定义 Artisan 命令。 + +事实上,大多数核心 Laravel 组件 包含服务提供器。应用程序所有注册在服务提供器的均列在 `app/config/app.php` 配置文件的 `providers` 数组中。 + +#### 定义一个服务提供器 + + +要创建服务提供器,简单的继承 `Illuminate\Support\ServiceProvider` 类并且定义一个 `register` 方法: + use Illuminate\Support\ServiceProvider; + + class FooServiceProvider extends ServiceProvider { + + public function register() + { + $this->app->bind('foo', function() + { + return new Foo; + }); + } + + } + +注意在 `register` 方法,应用程序通过 `$this->app` 属性访问 IoC 容器。一旦你已经创建了提供器并且想将它注册到应用程序中, 只需简单的放入 `app` 配置文件里 `providers` 数组中。 + +#### 运行时注册服务提供器 + +也可以使用 `App::register` 方法在运行时注册服务提供器: + + App::register('FooServiceProvider'); + + +## 容器事件 + +#### 注册获取事件监听者 + +容器在每次获取对象时都触发一个时间。你可以通过使用 `resolving` 方法来监听该事件: + + App::resolvingAny(function($object) + { + // + }); + + App::resolving('foo', function($foo) + { + // + }); + +注意获取的对象将会传入回调函数中。 diff --git a/cn/license.md b/cn/license.md new file mode 100644 index 0000000..aa1f3d4 --- /dev/null +++ b/cn/license.md @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright © Taylor Otwell + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/cn/lifecycle.md b/cn/lifecycle.md new file mode 100644 index 0000000..6034d4e --- /dev/null +++ b/cn/lifecycle.md @@ -0,0 +1,81 @@ +# 请求(Request)的生命周期 + +- [概述](#overview) +- [请求的生命周期](#request-lifecycle) +- [启动文件](#start-files) +- [应用程序事件](#application-events) + + +## 概述 + +在现实世界中使用工具时,如果理解了工具的工作原理,使用起来就会更加有底气。应用开发也是如此。当你理解了开发工具是如何工作的,使用起来就会更加自如。这篇文档的目标就是提供一个高层次的概述,使你对于Laravel框架的运行方式有一个较好的把握。在更好地了解了整个框架之后,框架的组件和功能就不再显得那么神秘,开发起应用来也更加得心应手。这篇文档包含了关于请求生命周期的高层次概述,以及启动文件和应用程序事件的相关内容。 + +如果你不能立即理解所有的术语,别灰心,可以先有一个大致的把握,在阅读文档其他章节的过程中继续积累和消化知识。 + + +## 请求的生命周期 + +发送给应用程序的所有请求都经由 `public/index.php` 脚本处理。如果使用的是 Apache 服务器,Laravel中包含的 `.htaccess` 文件将对所有请求进行处理并传递给 `index.php`。这是Laravel从接受客户端请求到返回响应给客户端的整个过程的开始。若能对于Laravel的引导过程(bootstrap process)有一个大致的认识,将有助于理解框架,我们不妨先讨论这个。 + +到目前为止,学习Laravel引导过程所需掌握的最重要的概念就是 **服务提供器**。打开 `app/config/app.php` 配置文件,找到 `providers` 数组,你会发现一个服务提供器的列表。这些提供器充当了Laravel的主要引导机制。在我们深入服务提供器之前,先回到 `index.php`的讨论。当一个请求进入 `index.php` 文件,`bootstrap/start.php` 文件会被加载。这个文件会创建一个 Laravel `Application` 对象,该对象同时作为框架的 [IoC 容器](/docs/ioc)。 + +`Application` 对象创建完成后,框架会设置一些路径信息并运行 [环境检测](/docs/configuration#environment-configuration) 。然后会执行位于Laravel源码内部的引导脚本,并根据你的配置文件设置时区、错误报告等其他信息。除了配置这些琐碎的配置选项以外,该脚本还会做一件非常重要的事情:注册所有为应用程序配置的服务提供器。 + +简单的服务提供器只包含一个方法:`register`。当应用程序对象通过自身的 `register` 方法注册某个服务提供器时,会调用该服务提供器的 `register` 方法。服务提供器通过这个方法向 [IoC 容器](/docs/ioc) 注册一些东西。从本质上讲,每个服务提供器都是将一个或多个 [闭包](http://us3.php.net/manual/en/functions.anonymous.php) 绑定到容器中,你可以通过这些闭包访问绑定到应用程序的服务。例如,`QueueServiceProvider` 注册了多个闭包以便使用与 [队列](/docs/queues) 相关的多个类。当然,服务提供器并不局限于向IoC容器注册内容,而是可以用于任何引导性质的任务。服务提供器可以注册事件监听器、视图合成器、Artisan命令等等。 + +在注册完所有服务提供器后,`app/start` 下的文件会被加载。最后,`app/routes.php` 文件会被加载。一旦 `routes.php` 文件被加载,Request 对象就被发送给应用程序对象,继而被派发到某个路由上。 + +我们总结一下: + +1. 请求进入 `public/index.php` 文件。 +2. `bootstrap/start.php` 文件创建应用程序对象并检测环境。 +3. 内部的 `framework/start.php` 文件配置相关设置并加载服务提供器。 +4. 加载应用程序 `app/start` 目录下的文件。 +5. 加载应用程序的 `app/routes.php` 文件。 +6. 将 Request 对象发送给应用程序对象,应用程序对象返回一个 Response 对象。 +7. 将 Response 对象发回客户端。 + +你应该已经掌握了 Laravel 应用程序是如何处理发来的请求的。下面我们来看一下启动文件。 + + +## 启动文件 + +应用程序的启动文件被存放在`app/start`目录中。默认情况下,该目录下包含三个文件:`global.php`、`local.php` 和 `artisan.php`文件。需要获取更多关于`artisan.php`的信息,可以参考文档[Artisan 命令行](/docs/commands#registering-commands)。 + +`global.php`启动文件默认包含一些基本项目,例如[日志](/docs/errors)的注册以及载入`app/filters.php` 文件。然而,你可以在该文件里做任何你想做的事情。无论在什么环境下,它都将会被自动包含进_每一个_request中。而`local.php` 文件仅在`local`环境下被执行。获取更多关于环境的信息,请查看文档[配置](/docs/configuration)。 + +当然,如果除了`local`环境你还有其他环境的话,你也可以为针对这些环境创建启动文件。这些文件将在应用程序运行在该环境中时被自动包含。假设你在 `bootstrap/start.php` 文件中配置了一个 `development` 环境,你可以创建一个 `app/start/development.php` 文件,在那个环境下任何进入应用程序的请求都会包含该文件。 + +### 启动文件里存放什么 + +启动文件主要用来存放任何“引导”性质的代码。例如,你可以在启动文件中注册视图合成器,配置日志信息,或是进行一些PHP设置等。具体做什么取决于你。当然了,把所有引导代码都丢到启动文件里会使启动文件变得杂乱。对于大型应用而言,或是启动文件显得太杂乱了,请考虑将某些引导代码移至 [服务提供器](/docs/ioc#service-providers) 中。 + + +## 应用程序事件 + +#### 注册应用程序事件 + +你还可以通过注册 `before`、`after`、`finish` 和 `shutdown`应用程序事件以便在处理request之前或后做一些操作: + + App::before(function($request) + { + // + }); + + App::after(function($request, $response) + { + // + }); + +这些事件的监听器会在每个到达应用程序的请求处理之前(`before`)或之后(`after`)运行。可以利用这些事件来设置全局过滤器(filter),或是对于发回客户端的响应(response)统一进行修改。你可以在某个启动文件中或者 [服务提供器](/docs/ioc#service-providers) 中注册这些事件。 + +You may also register a listener on the `matched` event, which is fired when an incoming request has been matched to a route but that route has not yet been executed: + + Route::matched(function($route, $request) + { + // + }); + +当来自应用程序的响应发送至客户端后会触发 `finish` 事件。这个事件适合处理应用程序所需的最后的收尾工作。当所有 `finish` 事件的监听器都执行完毕后会立即触发 `shutdown` 事件,如果想在脚本结束前再做一些事情,这是最后的机会。不过在大多数情况下,你都不需要用到这些事件。 + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/localization.md b/cn/localization.md new file mode 100644 index 0000000..aaf8329 --- /dev/null +++ b/cn/localization.md @@ -0,0 +1,105 @@ +# 本地化 + +- [简介](#introduction) +- [语言文件](#language-files) +- [基本用例](#basic-usage) +- [复数形式](#pluralization) +- [验证消息的本地化](#validation) +- [Overriding Package Language Files](#overriding-package-language-files) + + +## 简介 + +Laravel `Lang` 类提供非常方便的方法来从不同语言文件中取得字符串,允许在应用程序中支持多语言。 + + +## 语言文件 + +语言文字存放在 `app/lang` 目录。应用程序所要支持的语言都需要在此目录建立为子目录。 + + /app + /lang + /en + messages.php + /es + messages.php + +#### 语言文件示例 + +语言文件只是返回键值(字符串)对数组。例如: + + 'Welcome to our application' + ); + +#### 在运行时改变默认语言 + +应用程序默认语言配置在 `app/config/app.php`配置文件中 `locale` 配置项.你可以在任何时候使用 `App::setLocale` 方法来改变当前激活语言。 + + App::setLocale('es'); + +#### Setting The Fallback Language + +You may also configure a "fallback language", which will be used when the active language does not contain a given language line. Like the default language, the fallback language is also configured in the `app/config/app.php` configuration file: + + 'fallback_locale' => 'en', + + +## 基本用例 + +#### 从语言文件中获取文本 + + echo Lang::get('messages.welcome'); + +传给 `get` 方法字符串第一部分是语言文件名称,第二个部分是要取得文本的名字。 + +> **注意**: 如果语言不存在该文本,那么 `get` 方法会将键返回。 + +#### 文本中替换 + +可以在语言文件中定义占位符: + + 'welcome' => 'Welcome, :name', + +然后,将要替换的值传递给 `Lang::get` 方法的第二个参数: + + echo Lang::get('messages.welcome', array('name' => 'Dayle')); + +#### 判断语言文件是否存在该文本 + + if (Lang::has('messages.welcome')) + { + // + } + + +## 复数形式 + +复数形式是一个复杂的问题,因为不同的语言有着不同的复数形式规则。你可以通过简单的在语言文件中使用”管道“符来分开单数和复数文本形式: + + 'apples' => 'There is one apple|There are many apples', + +然后你就可以使用 `Lang::choice` 方法来取得文本: + + echo Lang::choice('messages.apples', 10); + +You may also supply a locale argument to specify the language. For example, if you want to use the Russian (ru) language: + + echo Lang::choice('товар|товара|товаров', $count, array(), 'ru'); + +由于Laravel翻译机制是用Symfony的翻译组件,你也可以非常简单的创建更加复杂的复数形式规则: + + 'apples' => '{0} There are none|[1,19] There are some|[20,Inf] There are many', + + + +## 验证 + +对于验证功能中需要本地化的错误信息和提示信息,请参阅 相关文档。 + + +## Overriding Package Language Files + +Many packages ship with their own language lines. Instead of hacking the package's core files to tweak these lines, you may override them by placing files in the `app/lang/packages/{locale}` directory. So, for example, if you need to override the English language lines in `messages.php` for a package named `skyrim/hearthfire`, you would place a language file at: `app/lang/packages/en/hearthfire.php`. In this file you would define only the language lines you wish to override. Any language lines you don't override will still be loaded from the package's language files. \ No newline at end of file diff --git a/cn/mail.md b/cn/mail.md new file mode 100644 index 0000000..5b7eb2d --- /dev/null +++ b/cn/mail.md @@ -0,0 +1,140 @@ +# 邮件 + +- [配置](#configuration) +- [基本用法](#basic-usage) +- [嵌入内联附件](#embedding-inline-attachments) +- [队列邮件](#queueing-mail) +- [邮件 & 本地开发环境](#mail-and-local-development) + + +## 配置 + +Laravel的邮件功能构建于流行的[SwiftMailer](http://swiftmailer.org)库之上,并提供了简介、高效的API。邮件配置信息在`app/config/mail.php`文件中,并提供了包含SMTP主机、端口和证书的配置选项,也可以为发送的邮件配置一个全局`from`(来自)地址。你可以使用任何的SMTP服务器。如果你希望使用PHP的`mail`函数来发送邮件,可以通过改变配置文件中的 `driver`为`mail`。另外还支持`sendmail` 。 + +### API Drivers + +Laravel also includes drivers for the Mailgun and Mandrill HTTP APIs. These APIs are often simpler and quicker than the SMTP servers. Both of these drivers require that the Guzzle 4 HTTP library be installed into your application. You can add Guzzle 4 to your project by adding the following line to your `composer.json` file: + + "guzzlehttp/guzzle": "~4.0" + +#### Mailgun Driver + +To use the Mailgun driver, set the `driver` option to `mailgun` in your `app/config/mail.php` configuration file. Next, create an `app/config/services.php` configuration file if one does not already exist for your project. Verify that it contains the following options: + + 'mailgun' => array( + 'domain' => 'your-mailgun-domain', + 'secret' => 'your-mailgun-key', + ), + +#### Mandrill Driver + +To use the Mailgun driver, set the `driver` option to `mandrill` in your `app/config/mail.php` configuration file. Next, create an `app/config/services.php` configuration file if one does not already exist for your project. Verify that it contains the following options: + + 'mandrill' => array( + 'secret' => 'your-mandrill-key', + ), + +### Log Driver + +If the `driver` option of your `app/config/mail.php` configuration file is set to `log`, all e-mails will be written to your log files, and will not actually be sent to any of the recipients. This is primarily useful for quick, local debugging and content verification. + + +## 基本用例 + +使用`Mail::send` 方法来发送一封邮件: + + Mail::send('emails.welcome', $data, function($message) + { + $message->to('foo@example.com', 'John Smith')->subject('Welcome!'); + }); + +传入`send`方法的第一个参数为生成邮件体所用的视图名。第二个参数`$data`是要传入视图的数据,第三个参数为闭包,允许你为邮件配置各种选项。 + +> **注意:** `$message`变量总是会传递到邮件视图中,它允许你给该邮件内容添加内联附件。因此应该避免向视图中传递命名为`message`的变量。 + +> **注解:** 内联附件(Inline Attachment):“内联附件”是指可以在邮件体中直接看到的附件,一般是文本或图片;“内联附件”与一般附件的区别在于:一般附件必须在点击之后才能查看。详细信息可以看[内联附件与一般附件的区别](http://www.mkyong.com/computer-tips/different-between-inline-and-attachment-in-email/)。 + +除了HTML视图,你还可以指定一个纯文本视图: + + Mail::send(array('html.view', 'text.view'), $data, $callback); + +或者,你可以通过`html`或`text`关键字指定唯一一个视图类型: + + Mail::send(array('text' => 'view'), $data, $callback); + +你还可以为邮件指定其他选项,例如邮件抄送者或者附件: + + Mail::send('emails.welcome', $data, function($m) + { + $m->from('us@example.com', 'Laravel'); + + $m->to('foo@example.com')->cc('bar@example.com'); + + $m->attach($pathToFile); + }); + +当你为邮件添加附件时,可以指定MIME类型和/或展示名: + + $m->attach($pathToFile, array('as' => $display, 'mime' => $mime)); + +> **注意:** 传递给`Mail::send`闭包的消息对象实例继承自SwiftMailer类,因此,你可以调用任何该类的方法来构建邮件内容。 + + +## 嵌入内联附件 + +邮件中嵌入图片通常都很麻烦;幸好Laravel提供了很简便的方法来为你的邮件添加图片,并取得相应的CID。 + +#### 在邮件视图中嵌入内联图像 + + + Here is an image: + + + + +#### 在邮件视图中嵌入原始数据 + + + Here is an image from raw data: + + + + +注意`$message`变量总会通过`Mail`类传递给邮件视图。 + + +## 队列邮件 + +#### 将一封邮件放入队列中 + +由于发送邮件有可能会使应用程序需要花费较长的响应时间,许多开发者选择将邮件放入队列并在后台发送。Laravel内建了[统一队列 API](/docs/queue)来简化此功能。只需调用`Mail`类的`queue`方法就可以将邮件放入队列中: + + Mail::queue('emails.welcome', $data, function($m) + { + $m->to('foo@example.com', 'John Smith')->subject('Welcome!'); + }); + +你还可以使用`later`方法指定延迟多少秒再发送邮件: + + Mail::later(5, 'emails.welcome', $data, function($m) + { + $m->to('foo@example.com', 'John Smith')->subject('Welcome!'); + }); + +如果你想将邮件放到一个指定的队列或"管道" ,可以使用`queueOn`和`laterOn`方法: + + Mail::queueOn('queue-name', 'emails.welcome', $data, function($m) + { + $m->to('foo@example.com', 'John Smith')->subject('Welcome!'); + }); + + +## 邮件 & 本地开发环境 + +当你开发需要发送邮件的应用时,在你本地或开发环境中通常需要禁用邮件发送功能。你可以通过调用`Mail::pretend` 方法或在 `app/config/mail.php` 配置文件中设置 `pretend` 选项为 `true` 达到这一目的。当邮件发送功能被置为 `pretend` 模式时,所有邮件都会被写入当前应用的log文件中,而不会发送给收件人。 + +#### 启用Pretend模式 + + Mail::pretend(); + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) \ No newline at end of file diff --git a/cn/migrations.md b/cn/migrations.md new file mode 100644 index 0000000..8f15f02 --- /dev/null +++ b/cn/migrations.md @@ -0,0 +1,112 @@ +# 迁移 & 数据填充 + +- [简介](#introduction) +- [创建迁移](#creating-migrations) +- [运行迁移](#running-migrations) +- [回滚迁移](#rolling-back-migrations) +- [数据库填充](#database-seeding) + + +## 简介 + +迁移是一种数据库的版本控制。它允许一个团队修改数据库结构,并在当前模式下保持最新。迁移通常和 [结构生成器](/docs/schema) 配对使用来管理您应用程序的结构。 + + +## 创建迁移 + +使用 Artisan 命令行的 `migrate:make` 命令创建一个迁移: + + php artisan migrate:make create_users_table + +所有的迁移都被存放在 `app/database/migrations` 目录下,并且包含一个时间戳允许框架检测迁移的顺序。 + +您可以在创建迁移的时候指定 `--path` 选项,指定的目录应该相对于安装目录的主目录: + + php artisan migrate:make foo --path=app/migrations + +`--table` 和 `--create` 选项可以被用来指定表名以及是否创建一个新表: + + php artisan migrate:make add_votes_to_user_table --table=users + + php artisan migrate:make create_users_table --create=users + + +## 运行迁移 + +#### 运行所有迁移 + + php artisan migrate + +#### 运行某个路径下的所有迁移 + + php artisan migrate --path=app/foo/migrations + +#### 运行某个包下的所有迁移 + + php artisan migrate --package=vendor/package + +> **注意:** 如果在运行迁移的时候收到一个 "class not found" 的错误,请尝试运行 `composer dump-autoload` 命令。 + +### Forcing Migrations In Production + +Some migration operations are destructive, meaning they may cause you to lose data. In order to protect you from running these commands against your production database, you will prompted for confirmation before these commands are executed. To force thse commands to run without a prompt, use the `--force` flag: + + php artisan migrate --force + + +## 回滚迁移 + +#### 回滚最后一次迁移 + + php artisan migrate:rollback + +#### 回滚所有迁移 + + php artisan migrate:reset + +#### 回滚所有迁移并重新运行所有迁移 + + php artisan migrate:refresh + + php artisan migrate:refresh --seed + + +## 数据库填充 + +Laravel 还包含一个简单的方式通过填充类使用测试数据填充您的数据库。所有的填充类都存放在 `app/database/seeds` 目录下。填充类可以以形式命名,但最好遵循一些合理的约束,比如 `UserTableSeeder` 等。默认情况下,一个 `DatabaseSeeder` 类以为您定义。在这个类中,您可以使用 `call` 函数运行其他填充类,允许您控制填充顺序。 + +#### 数据库填充类的例子 + + class DatabaseSeeder extends Seeder { + + public function run() + { + $this->call('UserTableSeeder'); + + $this->command->info('User table seeded!'); + } + + } + + class UserTableSeeder extends Seeder { + + public function run() + { + DB::table('users')->delete(); + + User::create(array('email' => 'foo@bar.com')); + } + + } + +使用 Artisan 命令行的 `db:seed` 命令填充数据库: + + php artisan db:seed + +默认情况下,`db:seed` 命令运行的是 `DatabaseSeeder` 类,这个类还会调用其他seed类。然而,你可以是使用 `--class` 参数来指定一个seed类: + + php artisan db:seed --class=UserTableSeeder + +您也可以使用 `migrate:refresh` 命令填充数据库,将会回滚并重新运行所有迁移: + + php artisan migrate:refresh --seed diff --git a/cn/packages.md b/cn/packages.md new file mode 100644 index 0000000..b0ca8ed --- /dev/null +++ b/cn/packages.md @@ -0,0 +1,237 @@ +# 包开发 + +- [简介](#introduction) +- [创建包](#creating-a-package) +- [包结构](#package-structure) +- [服务提供器](#service-providers) +- [Deferred Providers](#deferred-providers) +- [包规定](#package-conventions) +- [开发流程](#development-workflow) +- [包路由](#package-routing) +- [包配置](#package-configuration) +- [Package Views](#package-views) +- [包迁移](#package-migrations) +- [包Assets](#package-assets) +- [发布包](#publishing-packages) + + +## 简介 + +包是主要作用是为Laravel添加功能。包可以是任何东西,例如用于日期处理的[Carbon](https://github.com/briannesbitt/Carbon),或者是一个完整的BDD测试框架[Behat](https://github.com/Behat/Behat)。 + +当然,还有很多不同类型的包。有些包是独立的,这意味着它们可以在任何框架中工作,而不仅仅是Laravel。上面提到的Carbon和Behat就是独立的包。要在Laravel中使用这些包只需要在`composer.json`文件中指明。 + +另一方面,有些包仅支持Laravel。在上一个Laravel版本中,这些类型的包我们称为"bundles"。这些包可以包含专为增强Laravel应用的路由、控制器、视图、配置和迁移。由于开发独立的包不需要专门的过程,因此,本手册主要涵盖针对Laravel开发独立的包。 + +所有Laravel包都是通过[Packagist](http://packagist.org)和[Composer](http://getcomposer.org)发布的,因此很有必要学习这些PHP包发布工具。 + + +## 创建包 + +为Laravel创建一个包的最简单方式是使用Artisan的`workbench`命令。首先,你需要在`app/confg/workbench.php`文件中配置一些参数。在该文件中,你会看到`name`和`email`两个参数,这些值是用来为新创建的包生成`composer.json`文件的。一旦你提供了这些值,就可以开始构建一个新包了! + +#### 执行Artisan中的Workbench命令 + + php artisan workbench vendor/package --resources + +厂商名称(vendor name)是用来区分不同作者构建的相同名字的包。例如,如果我(Taylor Otwell)创建了个名为"Zapper"的包,厂商名就可以叫做`Taylor`,包名可以叫做`Zapper`。默认情况下,workbench命令建的包是不依赖任何框架的;然而,`resources`命令将会告诉workbench创建特定于Laravel的一些目录,例如`migrations`、`views`、`config`等。 + +一旦执行了`workbench`命令,新创建的包就会出现在Laravel安装目录下的`workbench`目录中。接下来就应该为你创建的包注册`ServiceProvider`了。你可以通过在`app/config/app.php`文件里的`provides`数组中添加该包。这将通知Laravel在应用程序开始启动时加载该包。服务提供者(Service providers)使用`[Package]ServiceProvider`样式的命名方式。所以,以上案例中,你需要将`Taylor\Zapper\ZapperServiceProvider`添加到`providers`数组。 + +一旦注册了provider,你就可以开始写代码了!然而,在此之前,建议你查看以下部分来了解更多关于包结构和开发流程的知识。 + + +## 包结构 + +执行`workbench`命令之后,你的包将被初始化规定的结构,并能够与laravel框架融合: + +#### 基本包目录结构 + + /src + /Vendor + /Package + PackageServiceProvider.php + /config + /lang + /migrations + /views + /tests + /public + +让我们来深入了解该结构。`src/Vendor/Package`目录是所有class的主目录,包括`ServiceProvider`。`config`、`lang`、`migrations`和`views`目录,就如你所猜测,包含了相应的资源。包可以包含这些资源中的任意几个,就像一个"regular"的应用。 + + +## 服务提供器 + +服务提供器只是包的引导类。默认情况下,他们包含两个方法:`boot`和`register`。你可以在这些方法内做任何事情,例如:包含路由文件、注册IoC容器的绑定、监听事件或者任何想做的事情。 + +`register`方法在服务提供器注册时被立即调用,而`boot`方法仅在请求被路由前调用。因此,如果服务提供器中的动作(action)依赖另一个已经注册的服务提供器,或者你正在覆盖另一个服务提供其绑定的服务,就应该使用`boot`方法。 + +当使用`workbench`命令创建包时,`boot`方法已经包含了如下的动作: + + $this->package('vendor/package'); + +该方法告诉Laravel如何为应用程序加载视图、配置或其他资源。通常情况下,你没有必要改变这行代码,因为它会根据workbench的默认约定将包设置好的。 + +默认情况下,一旦注册了一个包,那么它的资源可以通过package方法在`vendor/package`中找到。你也可以向 `package` 方法中传入第二个参数来重写这个方法。例如: + + // 向 `package` 方法中传入一个自定义的命名空间 + $this->package('vendor/package', 'custom-namespace'); + + //那么,这个包的资源现在可以通过这个自定义的命名空间来访问 + $view = View::make('custom-namespace::foo'); + +Laravel并没有为`service provider`提供“默认”的存放地点。您可以根据自己的喜好,将它们放置在任何地方,您也可以将它们统一组织在一个`Providers`命名空间里,并放置在应用的`app`目录下。这些文件可以被放置在任何地方,只需保证Composer的[自动加载组件](http://getcomposer.org/doc/01-basic-usage.md#autoloading)知道如何加载这些类。 + +If you have changed the location of your package's resources, such as configuration files or views, you should pass a third argument to the `package` method which specifies the location of your resources: + + $this->package('vendor/package', null, '/path/to/resources'); + + +## Deferred Providers + +If you are writing a service provider that does not register any resources such as configuration or views, you may choose to make your provider "deferred". A deferred service provider is only loaded and registered when one of the services it provides is actually needed by the application IoC container. If none of the provider's services are needed for a given request cycle, the provider is never loaded. + +To defer the execution of your service provider, set the `defer` property on the provider to `true`: + + protected $defer = true; + +Next you should override the `provides` method from the base `Illuminate\Support\ServiceProvider` class and return an array of all of the bindings that your provider adds to the IoC container. For example, if your provider registers `package.service` and `package.another-service` in the IoC container, your `provides` method should look like this: + + public function provides() + { + return array('package.service', 'package.another-service'); + } + + +## 包约定 + +要使用包中的资源,例如配置或视图,需要用双冒号语法: + +#### 加载包中的视图 + + return View::make('package::view.name'); + +#### 获取包的某个配置项 + + return Config::get('package::group.option'); + +> **注意:** 如果你包中包含迁移,请为迁移名(migration name)添加包名作为前缀,以避免与其他包中的类名冲突。 + + +## 开发流程 + +当开发一个包时,能够使用应用程序上文是相当有用的,这样将允许你很容易的解决视图模板的等问题。所以,我们开始,安装一个全新的Laravel框架,使用`workbench`命令创建包结构。 + +在使用`workbench`命令创建包后。`workbench/[vendor]/[package]`目录使用`git init`,`git push`!这将允许你在应用程序中方便开发而不用为`composer update`命令苦扰。 + +当包存放在`workbench`目录时,你可能担心Composer如何知道自动加载包文件。当`workbench`目录存在,Laravel将智能扫描该目录,在应用程序开始时加载它们的Composer自动加载文件! + + +## 包路由 + +在之前的Laravel版本中,`handlers`用来指定那个URI包会响应。然而,在Laravel4中,一个包可以相应任意URI。要在包中加载路由文件,只需在服务提供器的`boot`方法`include`它。 + +#### 在服务提供器中包含路由文件 + + public function boot() + { + $this->package('vendor/package'); + + include __DIR__.'/../../routes.php'; + } + + +## 包配置 + +#### 访问包配置文件 + +有时创建的包可能会需要配置文件。这些配置文件应该和应用程序配置文件相同方法定义。并且,当使用 `$this->package`方法来注册服务提供器时,那么就可以使用“双冒号”语法来访问: + + Config::get('package::file.option'); + +#### 访问包单一配置文件 + +然而,如果你包仅有一个配置文件,你可以简单命名为`config.php`。当你这么做时,你可以直接访问该配置项,而不需要特别指明文件名: + + Config::get('package::option'); + +#### Registering A Resource Namespace Manually + +Sometimes, you may wish to register package resources such as views outside of the typical `$this->package` method. Typically, this would only be done if the resources were not in a conventional location. To register the resources manually, you may use the `addNamespace` method of the `View`, `Lang`, and `Config` classes: + + View::addNamespace('package', __DIR__.'/path/to/views'); + +Once the namespace has been registered, you may use the namespace name and the "double colon" syntax to access the resources: + + return View::make('package::view.name'); + +The method signature for `addNamespace` is identical on the `View`, `Lang`, and `Config` classes. + +### 级联配置文件 + +但其他开发者安装你的包时,他们也许需要覆盖一些配置项。然而,如果从包源代码中改变值,他们将会在下次使用Composer更新包时又被覆盖。替代方法是使用artisan命令 `config:publish`: + + php artisan config:publish vendor/package + +当执行该命令,配置文件就会拷贝到`app/config/packages/vendor/package`,开发者就可以安全的更改配置项了。 + +> **注意:** 开发者也可以为该包创建指定环境的配置文件通过替换配置项并放置在`app/config/packages/vendor/package/environment. + + +## Package Views + +If you are using a package in your application, you may occasionally wish to customize the package's views. You can easily export the package views to your own `app/views` directory using the `view:publish` Artisan command: + + php artisan view:publish vendor/package + +This command will move the package's views into the `app/views/packages` directory. If this directory doesn't already exist, it will be created when you run the command. Once the views have been published, you may tweak them to your liking! The exported views will automatically take precedence over the package's own view files. + + +## 包迁移 + +#### 为工作台的包创建迁移 + +你可以很容易在包中创建和运行迁移。要为工作台里的包创建迁移,使用`--bench`选项: + + php artisan migrate:make create_users_table --bench="vendor/package" + +#### 为工作台包运行迁移 + + php artisan migrate --bench="vendor/package" + +#### 为已安装的包执行迁移 + +要为已经通过Composer安装在`vendor`目录下的包执行迁移,你可以直接使用`--package`: + + php artisan migrate --package="vendor/package" + + +## 包Assets + +#### 将包Assets移动到Public + +有些包可能含有assets,例如JavaScript,CSS,和图片。然而,我们无法链接到`vendor`或`workbench`目录里的assets,所以我们需要可以将这些assets移入应用程序的`public`目录。`asset:publish`命令可以实现: + + php artisan asset:publish + + php artisan asset:publish vendor/package + +如果这个包仍在`workbench`中,那么请使用`--bench` 指令: + + php artisan asset:publish --bench="vendor/package" + +这个命令将会把assets移入与“供应商”和“包名”相对应的`public/packages`目录下面。因此,包名为`userscape/kudos`的assets将会被移至`public/packages/userscape/kudos`。通过使用这个asset发布方法,可以让您安全的在包中的view内访问asset路径。 + + +## 发布包 + +当你创建的包准备发布时,你应该将包提交到[Packagist](http://packagist.org)仓库。如果你的包只针对Laravel,最好在包的`composer.json`文件中添加`laravel`标签。 + +还有,在发布的版本中添加tag,以便开发者能当请求你的包在他们`composer.json`文件中依赖稳定版本。如果稳定版本还没有好,考虑直接在Composer中使用`branch-alias`。 + +一旦你的包发布,舒心的继续开发通过`workbench`创建的应用程序上下文。这是很方便的方法来在发布包后继续开发包。 + +一些组织使用他们私有分支包为他们自己开发者。如果你对这感兴趣,查看Composer团队构建的[Satis](http://github.com/composer/satis)文档。 + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/pagination.md b/cn/pagination.md new file mode 100644 index 0000000..5cc1d6d --- /dev/null +++ b/cn/pagination.md @@ -0,0 +1,140 @@ +# 分页 + +- [配置](#configuration) +- [基本用法](#usage) +- [给分页链接添加自定义信息](#appending-to-pagination-links) +- [Converting To JSON](#converting-to-json) +- [Custom Presenters](#custom-presenters) + + +## 配置 + +在其它的框架中,分页有时很痛苦. 但是Laravel让分页简单到不可思议. 默认Laravel包含了两个分页视图, 在`app/config/view.php` 文件中的pagination选项中指定分页链接具体使用哪一个视图. + +`pagination::slider`视图 基于当前所在页数给出一个浮动的页数范围,`pagination::simple` 视图只是简单的给出 '上一页' '下一页' 两个链接. **两个视图都能完美的和bootstrap框架结合** + + +## 基本用法 + +Laravel有多种方式实现分页. 最简单的是在普通查询或Eloquent模型查询中使用 `paginate` 方法. + +#### 从数据库查询中分页 + + $users = DB::table('users')->paginate(15); + +#### 从一个 Eloquent 模型中分页 + +也可以从 [Eloquent](/docs/eloquent) 模型中分页: + + $allUsers = User::paginate(15); + + $someUsers = User::where('votes', '>', 100)->paginate(15); + +你只需要吧每页查询的记录数传给`paginate`方法即可. 在获得包含分页的查询结果集之后, 就可以在视图中展示了, 在视图中输出分页链接使用 `links` 方法: + +
+ + name; ?> + +
+ + links(); ?> + +以上就是创建一个分页链接的所需的所有信息了! 我们还没有提到Laravel做了什么. Laravel会自己把其它的事情做完. + +If you would like to specify a custom view to use for pagination, you may pass a view to the `links` method: + + links('view.name'); ?> + +你也可以通过下面的方法获取关于分页更加详尽的信息: + +- `getCurrentPage` +- `getLastPage` +- `getPerPage` +- `getTotal` +- `getFrom` +- `getTo` +- `count` + +#### "Simple Pagination" + +If you are only showing "Next" and "Previous" links in your pagination view, you have the option of using the `simplePaginate` method to perform a more efficient query. This is useful for larger datasets when you do not require the display of exact page numbers on your view: + + $someUsers = User::where('votes', '>', 100)->simplePaginate(15); + +#### 自定义分页 + +有时你可能希望自定义分页, 只需使用 `Paginator::make` 方法,并把 记录集合(当前页需要展示的数据集), 总记录数(int), 每页记录数(int) 作为参数: + + + $paginator = Paginator::make($items, $totalItems, $perPage); + +#### Customizing The Paginator URI + +You may also customize the URI used by the paginator via the `setBaseUrl` method: + + $users = User::paginate(); + + $users->setBaseUrl('custom/url'); + +The example above will create URLs like the following: http://example.com/custom/url?page=2 + + +## 给分页链接添加自定义信息 + +可以通过分页器的 `appends` 方法为分页链接添加上自定查询字符串: + + appends(array('sort' => 'votes'))->links(); ?> + +最后产生的url如下: + + http://example.com/something?page=2&sort=votes + +If you wish to append a "hash fragment" to the paginator's URLs, you may use the `fragment` method: + + fragment('foo')->links(); ?> + +This method call will generate URLs that look something like this: + + http://example.com/something?page=2#foo + + +## Converting To JSON + +The `Paginator` class implements the `Illuminate\Support\Contracts\JsonableInterface` contract and exposes the `toJson` method. You can may also convert a `Paginator` instance to JSON by returning it from a route. The JSON'd form of the instance will include some "meta" information such as `total`, `current_page`, `last_page`, `from`, and `to`. The instance's data will be available via the `data` key in the JSON array. + + +## Custom Presenters + +The default pagination presenter is Bootstrap compatible out of the box; however, you may customize this with a presenter of your choice. + +### Extending The Abstract Presenter + +Extend the `Illuminate\Pagination\Presenter` class and implement its abstract methods. An example presenter for Zurb Foundation might look like this: + + class ZurbPresenter extends Illuminate\Pagination\Presenter { + + public function getActivePageWrapper($text) + { + return '
  • '.$text.'
  • '; + } + + public function getDisabledTextWrapper($text) + { + return '
  • '.$text.'
  • '; + } + + public function getPageLinkWrapper($url, $page) + { + return '
  • '.$page.'
  • '; + } + + } + +### Using The Custom Presenter + +First, create a view in your `app/views` directory that will server as your custom presenter. Then, replace `pagination` option in the `app/config/view.php` configuration file with the new view's name. Finally, the following code would be placed in your custom presenter view: + + diff --git a/cn/queries.md b/cn/queries.md new file mode 100644 index 0000000..df76e42 --- /dev/null +++ b/cn/queries.md @@ -0,0 +1,314 @@ +# 查询生成器 + +- [简介](#introduction) +- [获取数据](#selects) +- [连接](#joins) +- [高级查询条件](#advanced-wheres) +- [聚合查询结果](#aggregates) +- [原始查询表达式](#raw-expressions) +- [创建数据](#inserts) +- [更新数据](#updates) +- [删除数据](#deletes) +- [union合并查询结果](#unions) +- [Pessimistic Locking](#pessimistic-locking) +- [缓存查询结果](#caching-queries) + + +## 简介 + +查询生成器 为操作数据库提供了一个方便,顺畅的的接口,它支持所有Laravel支持的数据库系统,并能够完成绝大部分查询任务。 + +> **注意:** 查询生成器 使用了PDO参数绑定传递的方式,从而避免sql注入攻击,也就是在使用参数时不需要进行保证安全性的过滤操作。 + + +## 获取数据 + +#### 获取一张表里的所有数据 + + $users = DB::table('users')->get(); + + foreach ($users as $user) + { + var_dump($user->name); + } + +#### 获取一张表里的一条数据 + + $user = DB::table('users')->where('name', 'John')->first(); + + var_dump($user->name); + +#### 获取一张表里的满足where条件的第一行数据的指定字段的值 + + $name = DB::table('users')->where('name', 'John')->pluck('name'); + +#### 以列表形式获取一张表里一个字段的值 + + $roles = DB::table('roles')->lists('title'); + +lists方法返回一个包含所有roles表的title字段的值的数组. 可以通过lists的第二个参数为返回的数组自定义键名: + + $roles = DB::table('roles')->lists('title', 'name'); + +#### 筛选查询结果 + + $users = DB::table('users')->select('name', 'email')->get(); + + $users = DB::table('users')->distinct()->get(); + + $users = DB::table('users')->select('name as user_name')->get(); + +#### 为已经建立的查询添加筛选 + + $query = DB::table('users')->select('name'); + + $users = $query->addSelect('age')->get(); + +#### 使用where条件语句 + + $users = DB::table('users')->where('votes', '>', 100)->get(); + +#### 使用or语句 + + $users = DB::table('users') + ->where('votes', '>', 100) + ->orWhere('name', 'John') + ->get(); + +#### 在Where语句中使用Between子句 + + $users = DB::table('users') + ->whereBetween('votes', array(1, 100))->get(); + +#### Using Where Not Between + + $users = DB::table('users') + ->whereNotBetween('votes', array(1, 100))->get(); + +#### 在Where语句中使用In子句,In的内容通过数组传递 + + $users = DB::table('users') + ->whereIn('id', array(1, 2, 3))->get(); + + $users = DB::table('users') + ->whereNotIn('id', array(1, 2, 3))->get(); + +#### 使用whereNull方法获取未被清除或未被初始化的记录(字段如果没有指定默认值将会是null) + + $users = DB::table('users') + ->whereNull('updated_at')->get(); + +#### Order By语句, Group By语句, 和 Having 语句筛选 + + $users = DB::table('users') + ->orderBy('name', 'desc') + ->groupBy('count') + ->having('count', '>', 100) + ->get(); + +#### Offset 和 Limit语句 + + $users = DB::table('users')->skip(10)->take(5)->get(); + + +## 连接 + +查询生成器 也可以用来建立数据连接操作,我们看看下面的例子: + +#### 简单连接语句 + + DB::table('users') + ->join('contacts', 'users.id', '=', 'contacts.user_id') + ->join('orders', 'users.id', '=', 'orders.user_id') + ->select('users.id', 'contacts.phone', 'orders.price') + ->get(); + +#### 左连接(Left Join)语句 + + DB::table('users') + ->leftJoin('posts', 'users.id', '=', 'posts.user_id') + ->get(); + +指定更多的连接条件: + + DB::table('users') + ->join('contacts', function($join) + { + $join->on('users.id', '=', 'contacts.user_id')->orOn(...); + }) + ->get(); + +If you would like to use a "where" style clause on your joins, you may use the `where` and `orWhere` methods on a join. Instead of comparing two columns, these methods will compare the column against a value: + + DB::table('users') + ->join('contacts', function($join) + { + $join->on('users.id', '=', 'contacts.user_id') + ->where('contacts.user_id', '>', 5); + }) + ->get(); + + +## 高级查询条件 + +#### where条件分组 + +有时你可能需要创建更高级的where查询,比如 "where exists"筛选 或者给where条件分组. 查询生成器 都能够很好的处理: + + DB::table('users') + ->where('name', '=', 'John') + ->orWhere(function($query) + { + $query->where('votes', '>', 100) + ->where('title', '<>', 'Admin'); + }) + ->get(); + +上面的查询将产生如下的 SQL: + + select * from users where name = 'John' or (votes > 100 and title <> 'Admin') + +#### where中的Exists语句 + + DB::table('users') + ->whereExists(function($query) + { + $query->select(DB::raw(1)) + ->from('orders') + ->whereRaw('orders.user_id = users.id'); + }) + ->get(); + +上面的查询将产生如下的 SQL: + + select * from users + where exists ( + select 1 from orders where orders.user_id = users.id + ) + + +## 聚合查询结果 + +查询生成器 提供了多个聚合方法, 比如 `count`, `max`, `min`, `avg`,和 `sum`. + +#### 使用聚合方法 + + $users = DB::table('users')->count(); + + $price = DB::table('orders')->max('price'); + + $price = DB::table('orders')->min('price'); + + $price = DB::table('orders')->avg('price'); + + $total = DB::table('users')->sum('votes'); + + +## 原始查询表达式 + +有时,你可能需要使用原始的查询表达式, 这种表达式需要直接插入最终的sql语句中, 所以,请特别注意防范sql注入! 使用 `DB::raw` 方法实现原始查询表达式: + +#### 使用原始查询表达式 + + $users = DB::table('users') + ->select(DB::raw('count(*) as user_count, status')) + ->where('status', '<>', 1) + ->groupBy('status') + ->get(); + +#### 将数据表中的某个字段+1或-1 + + DB::table('users')->increment('votes'); + + DB::table('users')->decrement('votes'); + + +## 创建数据 + +#### 插入一条数据 + + DB::table('users')->insert( + array('email' => 'john@example.com', 'votes' => 0) + ); + +#### 同时插入多条数据,同时让主键自增 + +如果数据表已经有主键了, 使用 `insertGetId` 方法插入数据,不需要主键字段信息: + + $id = DB::table('users')->insertGetId( + array('email' => 'john@example.com', 'votes' => 0) + ); + +> **注意:** 当使用 PostgreSQL 数据库系统时, insertGetId 方法要求主键字段名为 id + +#### 一次插入多条数据 + + DB::table('users')->insert(array( + array('email' => 'taylor@example.com', 'votes' => 0), + array('email' => 'dayle@example.com', 'votes' => 0), + )); + + +## 更新数据 + +#### 更新数据 + + DB::table('users') + ->where('id', 1) + ->update(array('votes' => 1)); + + +## 删除数据 + +#### 普通删除方式 + + DB::table('users')->where('votes', '<', 100)->delete(); + +#### 删除一张表的所有数据 + + DB::table('users')->delete(); + +#### 清空一张表 + + DB::table('users')->truncate(); + + +## union合并查询结果 + +#### 使用Union合并两次查询 + +查询生成器提供了一个快速的方式来 "union" 两个查询: + + $first = DB::table('users')->whereNull('first_name'); + + $users = DB::table('users')->whereNull('last_name')->union($first)->get(); + +`unionAll` 方法也是可用的, 它和 `union` 方法一样。 + + +## Pessimistic Locking + +The query builder includes a few functions to help you do "pessimistic locking" on your SELECT statements. + +To run the SELECT statement with a "shared lock", you may use the `sharedLock` method on a query: + + DB::table('users')->where('votes', '>', 100)->sharedLock()->get(); + +To "lock for update" on a SELECT statement, you may use the `lockForUpdate` method on a query: + + DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get(); + + +## 缓存查询结果 + +使用 `remember` 方法可以很容易的缓存查询结果: + + $users = DB::table('users')->remember(10)->get(); + +在上面的例子中, 查询结果将被缓存10分钟, 当某个查询的结果正在被缓存时, 该查询实际不会执行, 查询结果将直接从缓存系统中读取。 + +If you are using a [supported cache driver](/docs/cache#cache-tags), you can also add tags to the caches: + + $users = DB::table('users')->cacheTags(array('people', 'authors'))->remember(10)->get(); + +

    译者:苏小林 github

    diff --git a/cn/queues.md b/cn/queues.md new file mode 100644 index 0000000..e176958 --- /dev/null +++ b/cn/queues.md @@ -0,0 +1,255 @@ +# 队列 + +- [配置](#configuration) +- [基础用法](#basic-usage) +- [队列闭包](#queueing-closures) +- [运行队列监听器](#running-the-queue-listener) +- [Daemon Queue Worker](#daemon-queue-worker) +- [推送队列](#push-queues) +- [Failed Jobs](#failed-jobs) + + +## 配置 + +Laravel的队列组件为许多队列服务提供了统一的API接口。队列服务让你可以异步处理一个耗时任务,比如延迟发送一封邮件,从而大大加快了应用的Web请求处理速度。 + +队列的设置信息储存在 `app/config/queue.php` 文件中。在这个文件中你可以找到所有目前支持的队列驱动的连接设置,包括[Beanstalkd](http://kr.github.com/beanstalkd)、[IronMQ](http://iron.io)、[Amazon SQS](http://aws.amazon.com/sqs)、[Redis](http://redis.io)和同步处理(本地环境使用)驱动。 + +下面是相应队列驱动所需的依赖性包: + +- Beanstalkd: `pda/pheanstalk` +- Amazon SQS: `aws/aws-sdk-php` +- IronMQ: `iron-io/iron_mq` + + +## 基础用法 + +#### 推送一个任务到队列中 + +使用 `Queue::push` 方法推送一个新任务到队列中: + + Queue::push('SendEmail', array('message' => $message)); + +`push` 方法的第一个参数是用来处理任务的类的名称。第二个参数是一个数组,包含了需要传递给处理器的数据。一个任务处理器应该像这样定义: + + class SendEmail { + + public function fire($job, $data) + { + // + } + + } + +注意,类唯一需要的方法是 `fire` 方法,它接受一个 `Job` 实例就像 `data` 数组一样发送到队列。 + +#### 指定一个自定义处理器方法 + +如果你希望任务调用 `fire` 以外的方法,你可以在推送任务时指定相应方法: + + Queue::push('SendEmail@send', array('message' => $message)); + +*Specifying The Queue / Tube For A Job** + +You may also specify the queue / tube a job should be sent to: + + Queue::push('SendEmail@send', array('message' => $message), 'emails'); + +#### Passing The Same Payload To Multiple Jobs + +If you need to pass the same data to several queue jobs, you may use the `Queue::bulk` method: + + Queue::bulk(array('SendEmail', 'NotifyUser'), $payload); + +#### Delaying The Execution Of A Job + +Sometimes you may wish to delay the execution of a queued job. For instance, you may wish to queue a job that sends a customer an e-mail 15 minutes after sign-up. You can accomplish this using the `Queue::later` method: + + $date = Carbon::now()->addMinutes(15); + + Queue::later($date, 'SendEmail@send', array('message' => $message)); + +In this example, we're using the [Carbon](https://github.com/briannesbitt/Carbon) date library to specify the delay we wish to assign to the job. Alternatively, you may pass the number of seconds you wish to delay as an integer. + +#### 删除一个处理完的任务 + +一旦你处理完了一个任务,必须从队列中将它删除,可以通过 `Job` 实例中的 `delete` 方法完成这项工作: + + public function fire($job, $data) + { + // Process the job... + + $job->delete(); + } + +#### 将一个任务放回队列 + +如果你想要将一个任务放回队列,你可以使用 `release` 方法: + + public function fire($job, $data) + { + // Process the job... + + $job->release(); + } + +你也可以指定几秒后将任务放回队列: + + $job->release(5); + +#### 获取尝试处理任务的次数 + +如果任务在处理时发生异常,它会被自动放回队列。你可以使用 `attempts` 方法获取尝试处理任务的次数: + + if ($job->attempts() > 3) + { + // + } + +#### 获取任务ID + +你也可以获取任务的ID: + + $job->getJobId(); + + +## 队列闭包 + +你可以将一个闭包函数推送到队列中。这非常便于快速、简单地处理队列任务: + +#### 推送一个闭包函数到队列中 + + Queue::push(function($job) use ($id) + { + Account::delete($id); + + $job->delete(); + }); + +当使用 Iron.io [推送队列](#push-queues) 时,你需要特别谨慎地处理队列闭包。接受队列信息的结尾应该带有Iron.io的验证令牌。例如,推送队列的结尾应该类似: `https://yourapp.com/queue/receive?token=SecretToken`。你也许需要在封装队列请求前检查一下应用的秘密令牌的值。 + + +## 运行队列监听器 + +Laravel包含了一个用于运行已推送到队列的任务的Artisan服务。可以使用 `queue:listen` 命令来运行这个功能: + +#### 开启队列监听器 + + php artisan queue:listen + +你也可以指定队列监听器需要使用的连接: + + php artisan queue:listen connection + +注意一旦任务启动,它会一直运行除非你手动停止它。可以使用进程监视工具(例如 [Supervisor](http://supervisord.org/))来确保队列监听器处于运行状态。 + +You may pass a comma-delimited list of queue connections to the `listen` command to set queue priorities: + + php artisan queue:listen --queue=high,low + +In this example, jobs on the `high-connection` will always be processed before moving onto jobs from the `low-connection`. + +#### 设置任务的超时参数 + +You may also set the length of time (in seconds) each job should be allowed to run: + + php artisan queue:listen --timeout=60 + +#### Specifying Queue Sleep Duration + +另外,你还可以指定新任务轮询之前所需要等待的秒数: + + php artisan queue:listen --sleep=5 + +Note that the queue only "sleeps" if no jobs are on the queue. If more jobs are available, the queue will continue to work them without sleeping. + +如果只想处理队列的第一个任务,你可以使用 `queue:work` 命令: + +#### 处理队列的第一个任务 + +To process only the first job on the queue, you may use the `queue:work` command: + + php artisan queue:work + + +## Daemon Queue Workers + +The `queue:work` also includes a `--daemon` option for forcing the queue worker to continue processing jobs without ever re-booting the framework. This results in a significant reduction of CPU usage when compared to the `queue:listen` command, but at the added complexity of needing to drain the queues of currently executing jobs during your deployments. + +To start a queue worker in daemon mode, use the `--daemon` flag: + + php artisan queue:work connection --daemon + + php artisan queue:work connection --daemon --sleep=3 + + php artisan queue:work connection --daemon --sleep=3 --tries=3 + +As you can see, the `queue:work` command supports most of the same options available to `queue:listen`. You may use the `php artisan help queue:work` command to view all of the available options. + +### Deploying With Daemon Queue Workers + +The simplest way to deploy an application using daemon queue workers is to put the application in maintenance mode at the beginning of your deploymnet. This can be done using the `php artisan down` command. Once the application is in maintenance mode, Laravel will now accept any new jobs off of the queue, but will continue to process existing jobs. Once enough time has passed for all of your existing jobs to execute (usually no longer than 30-60 seconds), you may stop the worker and continue your deployment process. + +If you are using Supervisor or Laravel Forge, which utilizes Supervisor, you may typically stop a worker with a command like the following: + + supervisorctl stop worker-1 + +Once the queues have been drained and your fresh code has been deployed to your server, you should restart the daemon queue work. If you are using Supervisor, this can typically be done with a command like this: + + supervisorctl start worker-1 + + +## 推送队列 + +推送队列可以让你在没有守护进程和后台监听器的情况下使用 Laravel 4 强大的队列工具。当前,推送队列仅支持[Iron.io](http://iron.io)驱动。在开始前,创建一个 Iron.io 账户,然后将Iron的认证信息填入到 `app/config/queue.php` 配置文件中。 + +#### 注册一个推送队列订阅 + +接下来,你可以使用 `queue:subscribe` Artisan命令注册一个用来接收新的推送队列任务的URL结尾。 + + php artisan queue:subscribe queue_name http://foo.com/queue/receive + +现在,当你登录到Iron后台,你可以看见新的推送队列和订阅的URL。你可以为一个指定的队列订阅多个URL。接下来,为 `queue/receive` 结尾的URL创建一个路由,并且返回来自 `Queue::marshal` 方法的响应: + + Route::post('queue/receive', function() + { + return Queue::marshal(); + }); + +`marshal` 方法会自动执行相应的任务处理器类。想要处理推送队列上的任务,可以像处理一般的队列一样使用 `Queue::push` 方法。 + + +## Failed Jobs + +Since things don't always go as planned, sometimes your queued jobs will fail. Don't worry, it happens to the best of us! Laravel includes a convenient way to specify the maximum number of times a job should be attempted. After a job has exceeded this amount of attempts, it will be inserted into a `failed_jobs` table. The failed jobs table name can be configured via the `app/config/queue.php` configuration file. + +To create a migration for the `failed_jobs` table, you may use the `queue:failed-table` command: + + php artisan queue:failed-table + +You can specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:listen` command: + + php artisan queue:listen connection-name --tries=3 + +If you would like to register an event that will be called when a queue job fails, you may use the `Queue::failing` method. This event is a great opportunity to notify your team via e-mail or [HipChat](https://www.hipchat.com). + + Queue::failing(function($connection, $job, $data) + { + // + }); + +To view all of your failed jobs, you may use the `queue:failed` Artisan command: + + php artisan queue:failed + +The `queue:failed` command will list the job ID, connection, queue, and failure time. The job ID may be used to retry the failed job. For instance, to retry a failed job that has an ID of 5, the following command should be issued: + + php artisan queue:retry 5 + +If you would like to delete a failed job, you may use the `queue:forget` command: + + php artisan queue:forget 5 + +To delete all of your failed jobs, you may use the `queue:flush` command: + + php artisan queue:flush \ No newline at end of file diff --git a/cn/quick.md b/cn/quick.md index 63a94c2..53decd4 100644 --- a/cn/quick.md +++ b/cn/quick.md @@ -38,32 +38,33 @@ Typically, you may use a web server such as Apache or Nginx to serve your Larave php artisan serve -### Directory Structure +### 目录结构 -After installing the framework, take a glance around the project to familiarize yourself with the directory structure. The `app` directory contains folders such as `views`, `controllers`, and `models`. Most of your application's code will reside somewhere in this directory. You may also wish to explore the `app/config` directory and the configuration options that are available to you. +安装完框架后,你需要熟悉一下该项目的目录结构。`app` 文件夹包含了一些例如 `views` ,`controllers` 和 `models` 目录。 程序中大部分代码将要存放这些目录下。你也可以查看一下 `app/config` 文件夹里一些配置项目。 -## Routing +## 路由 -To get started, let's create our first route. In Laravel, the simplest route is a route to a Closure. Pop open the `app/routes.php` file and add the following route to the bottom of the file: +我们开始创建我们第一个路由。在 Laravel,简单路由的方法是闭包。打开 `app/routes.php` 文件加入如下代码: Route::get('users', function() { return 'Users!'; }); -Now, if you hit the `/users` route in your web browser, you should see `Users!` displayed as the response. Great! You've just created your first route. +现在,你在 web 浏览器输入 `/users`,你应该会看到 `Users!` 输出。真棒!已经创建了你第一个路由。 -Routes can also be attached to controller classes. For example: +路由也可以赋予控制器类。例如: Route::get('users', 'UserController@getIndex'); -This route informs the framework that requests to the `/users` route should call the `getIndex` method on the `UserController` class. For more information on controller routing, check out the [controller documentation](/docs/controllers). +该路由告知框架 `/users` 路由请求应该调用 `UserController` 类的 `getIndex` 方法。要查看更多关于路由控制器信息,查看 [控制器文档](/docs/controllers) 。 + -## Creating A View +## 创建视图 -Next, we'll create a simple view to display our user data. Views live in the `app/views` directory and contain the HTML of your application. We're going to place two new views in this directory: `layout.blade.php` and `users.blade.php`. First, let's create our `layout.blade.php` file: +接下来,我们要创建视图来显示我们用户数据。视图以HTML代码存放在 `app/views` 文件夹。我们将存放两个视图文件到该文件夹:`layout.blade.php` 和 `users.blade.php`。首先,让我们先创建 `layout.blade.php` 文件: @@ -73,7 +74,7 @@ Next, we'll create a simple view to display our user data. Views live in the `ap -Next, we'll create our `users.blade.php` view: +接着, 我们创建 `users.blade.php` 视图: @extends('layout') @@ -81,31 +82,32 @@ Next, we'll create our `users.blade.php` view: Users! @stop -Some of this syntax probably looks quite strange to you. That's because we're using Laravel's templating system: Blade. Blade is very fast, because it is simply a handful of regular expressions that are run against your templates to compile them to pure PHP. Blade provides powerful functionality like template inheritance, as well as some syntax sugar on typical PHP control structures such as `if` and `for`. Check out the [Blade documentation](/docs/templates) for more details. +这里的语法可能让你感到陌生。因为我们使用的是 Laravel 模板系统:Blade。Blade 非常快,因为仅使用了少量的正则表达式来为你的模板编译成原始PHP代码。Blade提供强大的功能,例如模板继承,还有一些常用的PHP控制结构语法糖,例如 `if` 和 `for`。 查看 [Blade 文档](/docs/templates) 了解更多。 -Now that we have our views, let's return it from our `/users` route. Instead of returning `Users!` from the route, return the view instead: +现在我们有了我们视图,让我们返回 `/users` 路由。我们用视图来替代返回 `Users!`: Route::get('users', function() { return View::make('users'); }); -Wonderful! Now you have setup a simple view that extends a layout. Next, let's start working on our database layer. +漂亮!现在你成功创建了继承至layout的视图。接下来,让我们开始数据库层。 + -## Creating A Migration +## 创建迁移 -To create a table to hold our data, we'll use the Laravel migration system. Migrations let you expressively define modifications to your database, and easily share them with the rest of your team. +要创建表来保存我们数据,我们将使用 Laravel 迁移系统。迁移描述数据库的改变,这让分享给他们团队成员非常简单。 -First, let's configure a database connection. You may configure all of your database connections from the `app/config/database.php` file. By default, Laravel is configured to use MySQL, and you will need to supply connection credentials within the database configuration file. If you wish, you may change the `driver` option to `sqlite` and it will use the SQLite database included in the `app/database` directory. +首先,我们配置数据库连接。你可以在 `app/config/database.php` 文件配置所有数据库连接信息。默认,Laravel 被配置为使用 SQLite,并且一个 SQLite 数据库存放在 `app/database` 目录。你可以将数据库配置文件的 `driver` 选项修改为 `mysql` 并且配置 `mysql` 连接信息。 -Next, to create the migration, we'll use the [Artisan CLI](/docs/artisan). From the root of your project, run the following from your terminal: +接下来,要创建迁移,我们将使用 [Artisan CLI](/docs/artisan)。在项目根目录中,在终端中执行以下命令: php artisan migrate:make create_users_table -Next, find the generated migration file in the `app/database/migrations` folder. This file contains a class with two methods: `up` and `down`. In the `up` method, you should make the desired changes to your database tables, and in the `down` method you simply reverse them. +然后,找到生成的迁移文件 `app/database/migrations` 目录。该文件包含了一个包含两个方法: `up` 和 `down` 的类。在 `up` 方法,你要指名数据库表的修改,在 `down` 方法中你只需要移除它。 -Let's define a migration that looks like this: +让我们定义如下迁移: public function up() { @@ -123,26 +125,26 @@ Let's define a migration that looks like this: Schema::drop('users'); } -Next, we can run our migrations from our terminal using the `migrate` command. Simply execute this command from the root of your project: +然后,我们在项目根目录中使用终端运行 `migrate` 命令来执行迁移: php artisan migrate -If you wish to rollback a migration, you may issue the `migrate:rollback` command. Now that we have a database table, let's start pulling some data! +如果你想回滚迁移,你可以执行 `migrate:rollback` 命令。现在我们已经有了数据库表,让我们让添加一些数据! ## Eloquent ORM -Laravel ships with a superb ORM: Eloquent. If you have used the Ruby on Rails framework, you will find Eloquent familiar, as it follows the ActiveRecord ORM style of database interaction. +Laravel 提供非常棒的 ORM:Eloquent。如果你使用过 Ruby on Rails 框架,你会发现 Eloquent 很相似,因为它遵循数据库交互的 ActiveRecord ORM 风格。 -First, let's define a model. An Eloquent model can be used to query an associated database table, as well as represent a given row within that table. Don't worry, it will all make sense soon! Models are typically stored in the `app/models` directory. Let's define a `User.php` model in that directory like so: +首先,让我们来定义个模型。ELoquent 模型可以用来查询相关数据表,以及表内的某一行。别着急,我们很快会谈及!模型通常存放在 `app/models` 目录。让我们在该目录定义个 `User.php` 模型,如: class User extends Eloquent {} -Note that we do not have to tell Eloquent which table to use. Eloquent has a variety of conventions, one of which is to use the plural form of the model name as the model's database table. Convenient! +注意我们并没有告诉 Eloquent 使用哪个表。Eloquent 有多种约定, 一个是使用模型的复数形式作为模型的数据库表。非常方便! -Using your preferred database administration tool, insert a few rows into your `users` table, and we'll use Eloquent to retrieve them and pass them to our view. +使用你喜欢的数据库管理工具,插入几行数据到 `users` 表,我们将使用 Eloquent 取得它们并传递到视图中。 -Now let's modify our `/users` route to look like this: +现在我们修改我们 `/users` 路由如下: Route::get('users', function() { @@ -151,14 +153,14 @@ Now let's modify our `/users` route to look like this: return View::make('users')->with('users', $users); }); -Let's walk through this route. First, the `all` method on the `User` model will retrieve all of the rows in the `users` table. Next, we're passing these records to the view via the `with` method. The `with` method accepts a key and a value, and is used to make a piece of data available to a view. +让我们来看看该路由。首先,`User` 模型的 `all` 方法将会从 `users` 表中取得所有记录。接下来,我们通过 `with` 方法将这些记录传递到视图。`with` 方法接受一个键和一个值,那么该值就可以在视图中使用了。 -Awesome. Now we're ready to display the users in our view! +激动啊。现在我们准备将用户显示在我们视图! -## Displaying Data +## 显示数据 -Now that we have made the `users` available to our view, we can display them like so: +现在我们视图中已经可以访问 `users` 类,我们可以如下显示它们: @extends('layout') @@ -168,6 +170,6 @@ Now that we have made the `users` available to our view, we can display them lik @endforeach @stop -You may be wondering where to find our `echo` statements. When using Blade, you may echo data by surrounding it with double curly braces. It's a cinch. Now, you should be able to hit the `/users` route and see the names of your users displayed in the response. +你可以发现没有找到 `echo` 语句。当使用 Blade 时,你可以使用两个花括号来输出数据。非常简单,你现在应该可以通过 `/users` 路由来查看到用户姓名作为响应输出。 -This is just the beginning. In this tutorial, you've seen the very basics of Laravel, but there are so many more exciting things to learn. Keep reading through the documentation and dig deeper into the powerful features available to you in [Eloquent](/docs/eloquent) and [Blade](/docs/templates). Or, maybe you're more interested in [Queues](/docs/queues) and [Unit Testing](/docs/testing). Then again, maybe you want to flex your architecture muscles with the [IoC Container](/docs/ioc). The choice is yours! +这仅仅是开始。在本系列教程中,你已经了解了 Laravel 基础部分,但是还有更让人兴奋的东西要学。继续阅读该文档并且深入[Eloquent](/docs/eloquent)和[Blade](/docs/templates)这些强大的特性。或者你对[队列](/docs/queues) 和 [单元测试](/docs/testing) 感兴趣。或许是你想了解[IoC Container](/docs/ioc), 选择权在于你! \ No newline at end of file diff --git a/cn/redis.md b/cn/redis.md new file mode 100644 index 0000000..8d4d183 --- /dev/null +++ b/cn/redis.md @@ -0,0 +1,81 @@ +# Redis + +- [简介](#introduction) +- [配置](#configuration) +- [用法](#usage) +- [批量输送](#pipelining) + + +## 简介 + +[Redis](http://redis.io) 是一个开源、先进的键值对(key-value)存储容器。由于它允许使用[strings](http://redis.io/topics/data-types#strings)、[hashes](http://redis.io/topics/data-types#hashes)、[lists](http://redis.io/topics/data-types#lists)、[sets](http://redis.io/topics/data-types#sets)和[sorted sets](http://redis.io/topics/data-types#sorted-sets)作为键(key),因此它经常被称为数据结构服务器。 + +> **注意:** 如果你是通过PECL为PHP安装的Redis扩展模块,那么,你必须在 `app/config/app.php` 文件中对其别名进行重新命名。 + + +## 配置 + +当前应用的Redis配置信息存储在 **app/config/database.php** 文件中。在此文件中,你可以看到一个 **redis** 数组,次数组中存储了当前使用Redis服务器信息。 + + 'redis' => array( + + 'cluster' => true, + + 'default' => array('host' => '127.0.0.1', 'port' => 6379), + + ), + +默认的服务器配置应该可以满足开发过程中的需求了。当然,你可以根据你的开发环境任意修改此配置数组。只需简单的为每个Redis服务器命名并制定此服务器所占用的host和port。 + +`cluster` 参数是告诉Laravel中的Redis客户端对所有的Redis节点执行客户端侧的分片(sharding),这就赋予你将创建一个节点池,并使用大量的RAM的能力。然而,客户端的分片机制不能够处理失效切换,因此,这种方式主要用来访问其它主数据容器中存放的缓存数据。 + + +## 用法 + +调用 `Redis::connection` 方法可以获取一个Redis类的实例: + + $redis = Redis::connection(); + +上面的代码可以获取一个到默认Redis服务器的连接。如果你没有使用服务器集群的话,你可以将服务器名称作为参数传递给 `connection` 方法,这样就可以获取Redis配置信息中的某个指定的服务器连接了: + + $redis = Redis::connection('other'); + +一旦获取到Redis类的实例,我们就可以向其发送任何[Redis命令](http://redis.io/commands) 了。Laravel使用一些魔术方法向Redis服务器传送命令: + + $redis->set('name', 'Taylor'); + + $name = $redis->get('name'); + + $values = $redis->lrange('names', 5, 10); + +注意,向Redis命令传递的参数以同样类似的方式传递给这些魔术方法。当然,如果你不用这些魔术方法,还可以使用 `command` 方法向服务器传送Redis命令: + + $values = $redis->command('lrange', array(5, 10)); + +当命令只是在默认连接上执行时,仅需使用 `Redis` 类中定义的静态方法即可: + + Redis::set('name', 'Taylor'); + + $name = Redis::get('name'); + + $values = Redis::lrange('names', 5, 10); + +> **注意:** Redis [cache](/docs/cache) 和 [session](/docs/session) 驱动都已经包含在Laravel中了。 + + +## 批量输送 + +批量输送(Pipelining)应当用于需要在一个操作中发送许多命令的场景。立即使用 `pipeline` 命令动手试试吧: + +#### 批量输送命令到服务器 + + Redis::pipeline(function($pipe) + { + for ($i = 0; $i < 1000; $i++) + { + $pipe->set("key:$i", $i); + } + }); + + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) \ No newline at end of file diff --git a/cn/releases.md b/cn/releases.md index 57668f2..4bfe470 100644 --- a/cn/releases.md +++ b/cn/releases.md @@ -1,7 +1,7 @@ # 版本说明 - [Laravel 4.2](#laravel-4.2) -- [Laravel 4.1](#laravel-4.1) +- [Laravel 4.1](#laravel-4.1) ## Laravel 4.2 diff --git a/cn/requests.md b/cn/requests.md new file mode 100644 index 0000000..a50f9d5 --- /dev/null +++ b/cn/requests.md @@ -0,0 +1,214 @@ +# 请求与输入 + +- [基本输入](#basic-input) +- [Cookies](#cookies) +- [用户提交信息持久化](#old-input) +- [文件上传](#files) +- [用户请求的详细信息](#request-information) + + +## 基本输入 + +Laravel使用一种简单的方式来访问用户提交的信息。 你可以用统一的方式来访问用户提交的信息,而不用为用户提交信息的方式操心。 + +#### 获取一个用户提交的值 + + $name = Input::get('name'); + +#### 为用户提交信息指定一个的默认返回值(如果用户未提交) + + $name = Input::get('name', 'Sally'); + +#### 判断指定的提交信息是否存在 + + if (Input::has('name')) + { + // + } + +#### 获取所有用户提交的信息 + + $input = Input::all(); + +#### 获取指定的信息,或者获取排除指定几个提交项之外的所有提交信息 + + $input = Input::only('username', 'password'); + + $input = Input::except('credit_card'); + +如果提交的表单含有 "数组" 形式的输入,可以使用点符号访问数组: + + $input = Input::get('products.0.name'); + +> **注意:** 有一些javascript库,比如 Backbone 会以json格式提交信息。 通过 `Input::get` 来获取信息,使用上无差别。 + + +## Cookies + +Laravel会加密所有已创建的cookie信息,并附加上授权码,当客户端擅自修改cookie信息时,该cookie将被废弃,从而保证安全性。 + +#### 获取一个指定的cookie值 + + $value = Cookie::get('name'); + +#### 添加一个新的cookie键值对 + + $response = Response::make('Hello World'); + + $response->withCookie(Cookie::make('name', 'value', $minutes)); + +#### 加入下一个Response的Cookie队列之中 + +如果想在Response创建之前设置cookie,可以使用 `Cookie::queue()` 方法。cookie将通过应用框架自动添加到最终的Response之中。 + + Cookie::queue($name, $value, $minutes); + +#### 创建一个永不过期的cookie键值对 + + $cookie = Cookie::forever('name', 'value'); + + +## 用户提交信息持久化 + +有时可能需要在用户的多个请求之间持久化用户提交的信息。 比如,当用户提交的信息验证失败重新返回提交信息页面时还原用户的输入。 + +#### 将用户提交的信息存入Session + + Input::flash(); + +#### 把指定的用户提交的信息存入Session + + Input::flashOnly('username', 'email'); + + Input::flashExcept('password'); + +如果你需要关联持久用户提交的信息的操作和重定向操作,可以使用如下的链式调用的方法: + + return Redirect::to('form')->withInput(); + + return Redirect::to('form')->withInput(Input::except('password')); + +> **注意:** 如果你想持久化其它的信息,请参考 [Session](/docs/session) 类. + +#### 获取已持久化的用户提交的信息 + + Input::old('username'); + + +## 文件上传 + +#### 获取用户上传的文件 + + $file = Input::file('photo'); + +#### 判断指定文件是否已经被上传 + + if (Input::hasFile('photo')) + { + // + } + +`file` 方法返回了一个 `Symfony\Component\HttpFoundation\File\UploadedFile` 类的实例, 该类继承自PHP的 `SplFileInfo` 类,并提供了大量操作该用户上传的文件的方法。 + +#### Determining If An Uploaded File Is Valid + + if (Input::file('photo')->isValid()) + { + // + } + +#### 移动一个已上传的文件 + + Input::file('photo')->move($destinationPath); + + Input::file('photo')->move($destinationPath, $fileName); + +#### 获取一个已上传的文件在服务器的真实路径 + + $path = Input::file('photo')->getRealPath(); + +#### 获取一个已上传的文件的大小 + + $size = Input::file('photo')->getSize(); + +#### 获取一个已上传的文件的 MIME 类型 + + $mime = Input::file('photo')->getMimeType(); + + +## 用户请求的详细信息 + +`Request` 类提供了许多 方法 用于获取关于请求的详细信息,该类继承自 `Symfony\Component\HttpFoundation\Request` 类。 下面提供了几个具有代表性的方法: + +#### 获取请求URI + + $uri = Request::path(); + +#### Retrieving The Request Method + + $method = Request::method(); + + if (Request::isMethod('post')) + { + // + } + +#### 判断请求路径是否符合指定模式 + + if (Request::is('admin/*')) + { + // + } + +#### 获取请求URL + + $url = Request::url(); + +#### 获取请求URI信息 + + $segment = Request::segment(1); + +#### 获取请求头里的Content-Type信息 + + $value = Request::header('Content-Type'); + +#### 获取 $_SERVER 数组里指定的值 + + $value = Request::server('PATH_INFO'); + +#### 判断请求是否使用https连接 + + if (Request::secure()) + { + // + } + +#### 判断是否是使用ajax请求 + + if (Request::ajax()) + { + // + } + +#### 判断请求是否有JSON Content Type + + if (Request::isJson()) + { + // + } + +#### 判断请求是否想要获取JSON + + if (Request::wantsJson()) + { + // + } + +#### 检测请求的响应格式 + +`Request::format` 方法基于 HTTP 请求头的 Accept 信息返回客户端希望获取的响应格式: + + if (Request::format() == 'json') + { + // + } diff --git a/cn/responses.md b/cn/responses.md new file mode 100644 index 0000000..b25dc56 --- /dev/null +++ b/cn/responses.md @@ -0,0 +1,223 @@ +# 视图 & Response + +- [基本Response](#basic-responses) +- [重定向](#redirects) +- [视图](#views) +- [视图合成](#view-composers) +- [特殊Response](#special-responses) +- [Response 宏](#response-macros) + + +## 基本Response + +#### 从路由中返回字符串 + + Route::get('/', function() + { + return 'Hello World'; + }); + +#### 创建自定义Response + +`Response`类继承自`Symfony\Component\HttpFoundation\Response`类,提供了多种方法用于构建HTTP Response。 + + $response = Response::make($contents, $statusCode); + + $response->header('Content-Type', $value); + + return $response; + +如果需要访问 `Response` 类的方法,但又要返回一个视图作为响应的内容,通过使用 `Response::view` 方法可以很容易实现: + + return Response::view('hello')->header('Content-Type', $type); + +#### 在Response中添加Cookie + + $cookie = Cookie::make('name', 'value'); + + return Response::make($content)->withCookie($cookie); + + +## 重定向 + +#### 返回一个重定向 + + return Redirect::to('user/login'); + +#### 返回一个带有数据的重定向 + + return Redirect::to('user/login')->with('message', 'Login Failed'); + +> **注意:** `with` 方法将数据写到了Session中,通过`Session::get` 方法即可获取该数据。 + +#### 返回一个重定向至命名路由 + + return Redirect::route('login'); + +#### 返回一个重定向至带有参数的命名路由 + + return Redirect::route('profile', array(1)); + +#### 返回一个重定向至带有命名参数的命名路由 + + return Redirect::route('profile', array('user' => 1)); + +#### 返回一个重定向至控制器Action + + return Redirect::action('HomeController@index'); + +#### 返回一个重定向至控制器Action并带有参数 + + return Redirect::action('UserController@profile', array(1)); + +#### 返回一个重定向至控制器Action并带有命名参数 + + return Redirect::action('UserController@profile', array('user' => 1)); + + +## 视图 + +视图通常包含应用中的HTML代码,为分离表现层与控制器和业务逻辑提供了便利。视图存放于`app/views`目录。 + +一个简单视图案例: + + + + + +

    Hello,

    + + + +通过如下方法来返回该视图到浏览器: + + Route::get('/', function() + { + return View::make('greeting', array('name' => 'Taylor')); + }); + +传递给`View::make`方法的第二个参数是一个数组,它将被传递给视图。 + +#### 传递数据给视图 + + // Using conventional approach + $view = View::make('greeting')->with('name', 'Steve'); + + // Using Magic Methods + $view = View::make('greeting')->withName('steve'); + +在上面的案例中,`$name`变量在视图内是可以访问的,其值为`Steve`。 + +你还可以在所有视图同共享同一数据: + + View::share('name', 'Steve'); + +#### 向视图传递子视图 + +或许你可能想将一个视图放入到另一个视图中。例如,将存放在`app/views/child/view.php`文件中的子视图传递给另一视图,如下: + + $view = View::make('greeting')->nest('child', 'child.view'); + + $view = View::make('greeting')->nest('child', 'child.view', $data); + +在父视图就可以输出该子视图了: + + + +

    Hello!

    + + + + + +## 视图合成器 + +视图合成器可以是回调函数或者类方法,它们在创建视图时被调用。如果你想在应用程序中,每次创建视图时都为其绑定一些数据,使用视图合成器可以将代码组织到一个地方。因此,视图合成器就好像是 “视图模型”或者是“主持人”。 + +#### 定义一个视图合成器 + + View::composer('profile', function($view) + { + $view->with('count', User::count()); + }); + +现在,每次创建`profile`视图时,`count`都会被绑定到视图中。 + +你也可以为多个视图同时绑定一个视图合成器: + + View::composer(array('profile','dashboard'), function($view) + { + $view->with('count', User::count()); + }); + +如果你更喜欢使用基于类的视图合成器,[IoC container](/docs/ioc)可以提供更多便利,如下所示: + + View::composer('profile', 'ProfileComposer'); + +视图合成器类定义如下: + + class ProfileComposer { + + public function compose($view) + { + $view->with('count', User::count()); + } + + } + +#### Defining Multiple Composers + +You may use the `composers` method to register a group of composers at the same time: + + View::composers(array( + 'AdminComposer' => array('admin.index', 'admin.profile'), + 'UserComposer' => 'user', + )); + +> **注意:** 没有规定视图合成器类存放在哪里。因此,你可以任意存放,只要能在`composer.json`文件中指定位置并自动加载即可。 + +### 视图创建器 + +视图 **创建器** 与视图合成器的工作方式几乎完全相同;区别在于当一个视图被实例化后就会立即触发视图创建器。视图创建器通过 `creator` 方法方便地定义: + + View::creator('profile', function($view) + { + $view->with('count', User::count()); + }); + + +## 特殊Response + +#### 创建一个JSON Response + + return Response::json(array('name' => 'Steve', 'state' => 'CA')); + +#### 创建一个JSONP Response + + return Response::json(array('name' => 'Steve', 'state' => 'CA'))->setCallback(Input::get('callback')); + +#### 创建一个文件下载Response + + return Response::download($pathToFile); + + return Response::download($pathToFile, $status, $headers); + +> **注意:** Symfony HttpFoundation 用于处理文件下载,要求下载的文件的文件名只包含 ASCII 字符。 + + +## Response 宏 + +如果希望自定义一个 response ,以便在你应用程序中的许多路由和控制器中进行重用,可以使用 `Response::macro` 方法: + + Response::macro('caps', function($value) + { + return Response::make(strtoupper($value)); + }); + +`macro` 方法接受两个参数,一个指定和名称和一个闭包。当通过 `Response` 类调用该名称的宏时,闭包就会被执行: + + return Response::caps('foo'); + +你可以在 `app/start` 目录里的文件中定义宏。或者,你也可以通过一个单独的文件组织你的宏,并将该文件包含至某个 `start` 文件中。 + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/routing.md b/cn/routing.md new file mode 100644 index 0000000..685b183 --- /dev/null +++ b/cn/routing.md @@ -0,0 +1,360 @@ +# 路由 + +- [基本路由](#basic-routing) +- [路由参数](#route-parameters) +- [路由过滤器](#route-filters) +- [命名路由](#named-routes) +- [路由组](#route-groups) +- [子域名路由](#sub-domain-routing) +- [路由前缀](#route-prefixing) +- [路由与模型绑定](#route-model-binding) +- [抛出 404 错误](#throwing-404-errors) +- [控制器路由](#routing-to-controllers) + + +## 基本路由 + +应用中的大多数路都会定义在 `app/routes.php` 文件中。最简单的Laravel路由由URI和闭包回调函数组成。 + +#### 基本 GET 路由 + + Route::get('/', function() + { + return 'Hello World'; + }); + +#### 基本 POST 路由 + + Route::post('foo/bar', function() + { + return 'Hello World'; + }); + +#### Registering A Route For Multiple Verbs + + Route::match(array('GET', 'POST'), '/', function() + { + return 'Hello World'; + }); + +#### 注册一个可以响应任何HTTP动作的路由 + + Route::any('foo', function() + { + return 'Hello World'; + }); + +#### 仅支持HTTPS的路由 + + Route::get('foo', array('https', function() + { + return 'Must be over HTTPS'; + })); + +实际开发中经常需要根据路由生成 URL,`URL::to`方法就可以满足此需求: + + $url = URL::to('foo'); + + +## 路由参数 + + Route::get('user/{id}', function($id) + { + return 'User '.$id; + }); + +#### 可选路由参数 + + Route::get('user/{name?}', function($name = null) + { + return $name; + }); + +#### 带有默认值的可选路由参数 + + Route::get('user/{name?}', function($name = 'John') + { + return $name; + }); + +#### 用正则表达式限定的路由参数 + + Route::get('user/{name}', function($name) + { + // + }) + ->where('name', '[A-Za-z]+'); + + Route::get('user/{id}', function($id) + { + // + }) + ->where('id', '[0-9]+'); + +#### 传递参数限定的数组 + +当然,必要的时候你还可以传递一个包含参数限定的数组作为参数: + + Route::get('user/{id}/{name}', function($id, $name) + { + // + }) + ->where(array('id' => '[0-9]+', 'name' => '[a-z]+')) + +#### 定义全局模式 + +如果希望在全局范围用指定正则表达式限定路由参数,可以使用 `pattern` 方法: + + Route::pattern('id', '[0-9]+'); + + Route::get('user/{id}', function($id) + { + // Only called if {id} is numeric. + }); + +#### 访问路由参数 + +如果想在路由范围外访问路由参数,可以使用 `Route::input` 方法: + + Route::filter('foo', function() + { + if (Route::input('id') == 1) + { + // + } + }); + + +## 路由过滤器 + +路由过滤器提供了非常方便的方法来限制对应用程序中某些功能访问,例如对于需要验证才能访问的功能就非常有用。Laravel框架自身已经提供了一些过滤器,包括 `auth`过滤器、`auth.basic`过滤器、`guest`过滤器以及`csrf`过滤器。这些过滤器都定义在`app/filter.php`文件中。 + + +#### 定义一个路由过滤器 + + Route::filter('old', function() + { + if (Input::get('age') < 200) + { + return Redirect::to('home'); + } + }); + +如果从路由过滤器中返回了一个response,那么该response将被认为对应的是此次request,路由将不会被执行,并且,此路由中所有定义在此过滤器之后的代码也都不会被执行。 + +#### 为路由绑定过滤器 + + Route::get('user', array('before' => 'old', function() + { + return 'You are over 200 years old!'; + })); + +#### 将过滤器绑定为控制器Action + + Route::get('user', array('before' => 'old', 'uses' => 'UserController@showProfile')); + +#### 为路由绑定多个过滤器 + + Route::get('user', array('before' => 'auth|old', function() + { + return 'You are authenticated and over 200 years old!'; + })); + +#### Attaching Multiple Filters Via Array + + Route::get('user', array('before' => array('auth', 'old'), function() + { + return 'You are authenticated and over 200 years old!'; + })); + +#### 指定过滤器参数 + + Route::filter('age', function($route, $request, $value) + { + // + }); + + Route::get('user', array('before' => 'age:200', function() + { + return 'Hello World'; + })); + +所有其后的过滤器将接收到 `$response`作为第三个参数: + + Route::filter('log', function($route, $request, $response) + { + // + }); + +#### 基于模式的过滤器 + +你也可以指针对URI为一组路由指定过滤器。 + + Route::filter('admin', function() + { + // + }); + + Route::when('admin/*', 'admin'); + +上述案例中,`admin`过滤器将会应用到所有以`admin/`开头的路由中。星号是通配符,将会匹配任意多个字符的组合。 + +还可以针对HTTP动作限定模式过滤器: + + Route::when('admin/*', 'admin', array('post')); + +#### 过滤器类 + +过滤器的高级用法中,还可以使用类来替代闭包函数。由于过滤器类是通过[IoC container](/docs/ioc)实现解析的,所有,你可以在这些过滤器中利用依赖注入(dependency injection)的方法实现更好的测试能力。 + +#### 注册一个基于类的过滤器 + + Route::filter('foo', 'FooFilter'); + +默认的,`FooFilter`类里的`filter`方法将被调用: + + class FooFilter { + + public function filter() + { + // Filter logic... + } + + } + +如果你不希望使用`filter` 方法,只用指定另一个方法即可: + Route::filter('foo', 'FooFilter@foo'); + + +## 命名路由 + +重定向和生成URL时,使用命名路由会更方便。你可以为路由指定一个名字,如下所示: + + Route::get('user/profile', array('as' => 'profile', function() + { + // + })); + +还可以为 controller action指定路由名称: + + Route::get('user/profile', array('as' => 'profile', 'uses' => 'UserController@showProfile')); + +现在,你可以使用路由名称来创建URL和重定向: + + $url = URL::route('profile'); + + $redirect = Redirect::route('profile'); + +可以使用`currentRouteName`方法来获取当前运行的路由名称: + + $name = Route::currentRouteName(); + + +## 路由组 + +有时你可能需要为一组路由应用过滤器。使用路由组就可以避免单独为每个路由指定过滤器了: + + Route::group(array('before' => 'auth'), function() + { + Route::get('/', function() + { + // Has Auth Filter + }); + + Route::get('user/profile', function() + { + // Has Auth Filter + }); + }); + +You may also use the `namespace` parameter within your `group` array to specify all controllers within that group as being in a given namespace: + + Route::group(array('namespace' => 'Admin'), function() + { + // + }); + + +## 子域名路由 + +Laravel中的路由功能还支持通配符子域名,你可以在域名中指定通配符参数: + +#### 注册子域名路由 + + Route::group(array('domain' => '{account}.myapp.com'), function() + { + + Route::get('user/{id}', function($account, $id) + { + // + }); + + }); + +## 路由前缀 + +可以通过`prefix`属性为组路由设置前缀: + + Route::group(array('prefix' => 'admin'), function() + { + + Route::get('user', function() + { + // + }); + + }); + + +## 路由与模型绑定 + +模型绑定,为在路由中注入模型实例提供了便捷的途径。例如,你可以向路由中注入匹配用户ID的整个模型实例,而不是仅仅注入用户ID。首先,使用 `Route::model` 方法指定要被注入的模型: + +#### 将参一个模型 + + Route::model('user', 'User'); + +然后,定义一个包含`{user}`参数的路由: + + Route::get('profile/{user}', function(User $user) + { + // + }); + +由于我们已将`{user}`参数绑定到了`User`模型,因此可以向路由中注入一个`User`实例。例如,对`profile/1`的访问将会把ID为1的`User`实例注入到路由中。 + +> **注意:** 如果在数据库中无法匹配到对应的模型实例,404错误将被抛出。 + +如果你希望自定义"not found"行为,可以通过传递一个闭包函数作为 `model` 方法的第三个参数: + + Route::model('user', 'User', function() + { + throw new NotFoundHttpException; + }); + +如果你想自己实现路由参数的解析,只需使用`Route::bind`方法即可: + + Route::bind('user', function($value, $route) + { + return User::where('name', $value)->first(); + }); + + +## 抛出 404 错误 + +有两种从路由中手动触发404错误的方法。首先,你可以使用`App::abort`方法: + + App::abort(404); + +其次,你可以抛出`Symfony\Component\HttpKernel\Exception\NotFoundHttpException`异常。 + +更多关于处理404异常以及错误发生时自定义response的信息可以查看[错误](/docs/errors#handling-404-errors)文档。 + + +## 控制器路由 + +Laravel不光提供了利用闭包函数处理路由的功能,还可以路由到控制器,甚至支持创建 [resource controllers](/docs/controllers#resource-controllers)。 + +参见文档 [Controllers](/docs/controllers) 以获取更多信息。 + +译者:王赛 [(Bootstrap中文网)](http://www.bootcss.com) diff --git a/cn/schema.md b/cn/schema.md new file mode 100644 index 0000000..2018467 --- /dev/null +++ b/cn/schema.md @@ -0,0 +1,218 @@ +# 结构生成器 + +- [简介](#introduction) +- [创建和删除表](#creating-and-dropping-tables) +- [添加字段](#adding-columns) +- [重命名字段](#renaming-columns) +- [删除字段](#dropping-columns) +- [检查存在性](#checking-existence) +- [添加索引](#adding-indexes) +- [外键](#foreign-keys) +- [删除索引](#dropping-indexes) +- [Dropping Timestamps & Soft Deletes](#dropping-timestamps) +- [存储引擎](#storage-engines) + + +## 简介 + +Laravel 的 `Schema` 类提供了一种与数据库无关的方式维护表。它和 Laravel 所支持的所有数据库都能很好的工作,并且提供了统一的接口。 + + +## 创建和删除表 + +使用 `Schema::create` 创建一个数据库的表: + + Schema::create('users', function($table) + { + $table->increments('id'); + }); + +传递给 `create` 函数的第一个参数是表的名字,第二个参数是一个闭包,将接受一个 `Blueprint` 对象用于定义新的表。 + +使用 `rename` 函数重命名一个已存在的表: + + Schema::rename($from, $to); + +使用 `Schema::connection` 函数指定结构操作所使用的数据库连接: + + Schema::connection('foo')->create('users', function($table) + { + $table->increments('id'); + }); + +使用 `Schema::drop` 函数删除一个表: + + Schema::drop('users'); + + Schema::dropIfExists('users'); + + +## 添加字段 + +使用 `Schema::table` 函数更新一个已存在的表: + + Schema::table('users', function($table) + { + $table->string('email'); + }); + + +表生成器包含一系列的字段类型用于构建表: + +命令 | 描述 +------------- | ------------- +`$table->bigIncrements('id');` | Incrementing ID using a "big integer" equivalent. +`$table->bigInteger('votes');` | BIGINT equivalent to the table +`$table->binary('data');` | BLOB equivalent to the table +`$table->boolean('confirmed');` | BOOLEAN equivalent to the table +`$table->char('name', 4);` | CHAR equivalent with a length +`$table->date('created_at');` | DATE equivalent to the table +`$table->dateTime('created_at');` | DATETIME equivalent to the table +`$table->decimal('amount', 5, 2);` | DECIMAL equivalent with a precision and scale +`$table->double('column', 15, 8);` | DOUBLE equivalent with precision +`$table->enum('choices', array('foo', 'bar'));` | ENUM equivalent to the table +`$table->float('amount');` | FLOAT equivalent to the table +`$table->increments('id');` | Incrementing ID to the table (primary key). +`$table->integer('votes');` | INTEGER equivalent to the table +`$table->longText('description');` | LONGTEXT equivalent to the table +`$table->mediumInteger('numbers');` | MEDIUMINT equivalent to the table +`$table->mediumText('description');` | MEDIUMTEXT equivalent to the table +`$table->morphs('taggable');` | Adds INTEGER `taggable_id` and STRING `taggable_type` +`$table->nullableTimestamps();` | Same as `timestamps()`, except allows NULLs +`$table->smallInteger('votes');` | SMALLINT equivalent to the table +`$table->tinyInteger('numbers');` | TINYINT equivalent to the table +`$table->softDeletes();` | Adds **deleted\_at** column for soft deletes +`$table->string('email');` | VARCHAR equivalent column +`$table->string('name', 100);` | VARCHAR equivalent with a length +`$table->text('description');` | TEXT equivalent to the table +`$table->time('sunrise');` | TIME equivalent to the table +`$table->timestamp('added_on');` | TIMESTAMP equivalent to the table +`$table->timestamps();` | Adds **created\_at** and **updated\_at** columns +`->nullable()` | Designate that the column allows NULL values +`->default($value)` | Declare a default value for a column +`->unsigned()` | Set INTEGER to UNSIGNED + + +#### 在 MySQL 中使用 After + +如果你使用 MySQL 数据库,您可以使用 `after` 函数指明字段的顺序: + + $table->string('name')->after('email'); + + +## 重命名字段 + +使用 `renameColumn` 函数重命名一个字段: + + Schema::table('users', function($table) + { + $table->renameColumn('from', 'to'); + }); + +> **注意:** 不支持重命名 `enum` 字段类型. + + +## 删除字段 + +#### 从表中删除一个字段 + + Schema::table('users', function($table) + { + $table->dropColumn('votes'); + }); + +#### 从表中删除多个字段 + + Schema::table('users', function($table) + { + $table->dropColumn('votes', 'avatar', 'location'); + }); + + +## 检查存在性 + +#### 检查表是否存在 + +您可以使用 `hasTable` 和 `hasColumn` 检查一个表或一个字段是否存在: + + if (Schema::hasTable('users')) + { + // + } + +#### 检查字段是否存在 + + if (Schema::hasColumn('users', 'email')) + { + // + } + + +## 添加索引 + +结构生成器支持多种类型的索引,有两种方法可以添加它们。首先,您可以在字段定义后链式的定义它们,或者独立的添加它们: + + $table->string('email')->unique(); + +或者,您可以选择在不同的行添加索引。下面是全部支持的索引类型: + +命令 | 描述 +------------- | ------------- +`$table->primary('id');` | 添加一个主键 +`$table->primary(array('first', 'last'));` | 添加组合键 +`$table->unique('email');` | 添加唯一键 +`$table->index('state');` | 添加一个索引 + + +## 外键 + +Laravel 也支持向表中添加外键约束: + + $table->foreign('user_id')->references('id')->on('users'); + +在这个例子中,我们指明 `user_id` 字段参照 `users` 表中的 `id` 字段。 + +您也可以指明 "on delete" 以及 "on update" 行为选项: + + $table->foreign('user_id') + ->references('id')->on('users') + ->onDelete('cascade'); + +可以使用 `dropForeign` 函数删除一个外键。像其他索引一样,一个相似的命名惯例被使用于外键的命名: + + $table->dropForeign('posts_user_id_foreign'); + +> **注意:** 当创建一个参照递增整数类型的外键的时候,记得把外键字段的类型定义为无符号。 + + +## 删除索引 + +为了删除索引,必须指明索引的名字。Laravel 默认为索引分配了一个合理的名字。通过连接表明、索引的字段名以及索引类型的形式。这里是一些例子: + +命令 | 描述 +------------- | ------------- +`$table->dropPrimary('users_id_primary');` | 从 "users" 表中删除一个主键 +`$table->dropUnique('users_email_unique');` | 从 "users" 表中删除一个唯一键 +`$table->dropIndex('geo_state_index');` | 从 "geo" 表中删除一个索引 + + +## Dropping Timestamps & SoftDeletes + +To drop the `timestamps`, `nullableTimestamps` or `softDeletes` column types, you may use the following methods: + +Command | Description +------------- | ------------- +`$table->dropTimestamps();` | Dropping the **created\_at** and **updated\_at** columns from the table +`$table->dropSoftDeletes();` | Dropping **deleted\_at** column from the table + + +## 存储引擎 + +通过在结构生成器设置 `engine` 属性为表设置存储引擎: + + Schema::create('users', function($table) + { + $table->engine = 'InnoDB'; + + $table->string('email'); + }); diff --git a/cn/security.md b/cn/security.md new file mode 100644 index 0000000..c937c2f --- /dev/null +++ b/cn/security.md @@ -0,0 +1,312 @@ +# 安全 + +- [配置](#configuration) +- [保存密码](#storing-passwords) +- [验证用户](#authenticating-users) +- [手动登陆用户](#manually) +- [保护路由](#protecting-routes) +- [HTTP基本验证](#http-basic-authentication) +- [密码提示和重置](#password-reminders-and-reset) +- [加密](#encryption) +- [Authentication Drivers](#authentication-drivers) + + +## 配置 + +Laravel 旨在让验证实现简单。事实上,大部分都已经配置好了。验证配置文件位于 `app/config/auth.php`,该文件包含了一些文档说明非常齐全的配置选项,通过它们可以调整用户验证的具体形式。 + +一般情况下,Laravel在`app/models`目录下包含有一个`User` 模型,通常被作为默认的Eloquent验证驱动所使用。请注意在构建该数据库结构模型时确保密码字段至少能容纳60个字符。 + +如果你的应用中不使用 Eloquent,你可以使用 Laravel 查询构造器来使用 `数据库` 验证驱动。 + +> **Note:** Before getting started, make sure that your `users` (or equivalent) table contains a nullable, string `remember_token` column of 100 characters. This column will be used to store a token for "remember me" sessions being maintained by your application. + + +## 保存密码 + +Laravel `Hash` 类提供了可靠的Bcrypt散列算法: + +#### 使用Bcrypt散列密码 + + $password = Hash::make('secret'); + +#### 验证散列密码 + + if (Hash::check('secret', $hashedPassword)) + { + // 密码匹配... + } + +#### 检查密码是否需要重新散列 + + if (Hash::needsRehash($hashed)) + { + $hashed = Hash::make('secret'); + } + + +## 用户验证 + +要在应用程序中登陆用户,使用 `Auth::attempt` 方法。 + + if (Auth::attempt(array('email' => $email, 'password' => $password))) + { + return Redirect::intended('dashboard'); + } + +注意 `email` 不是必需选项,这里仅作为示例。你应该使用任意数据库字段名来作为"用户名"。 `Redirect::intended` 方法会将用户请求重定向至被用户验证过滤器拦截之前用户试图访问URL中去。可以给该方法提供个回退URI,用于在访问目的无效时,重定向到该URI。 + +在调用 `attempt` 方法时,`auth.attempt` [事件](/docs/events) 将会触发。如果验证成功以及用户登陆了,`auth.login` 事件也会被触发。 + +#### 判断用户是否已经验证 + +要在应用程序中判断用户是否已经登陆,可以使用 `check` 方法: + + if (Auth::check()) + { + // 用户已经登陆... + } + +#### 验证用户并且“记住”她们 + +如果你想在应用中提供“记住我”功能,你可以传递true作为第二个参数传递给attempt方法,应用程序将会无期限地保持用户验证状态(除非手动退出): + + if (Auth::attempt(array('email' => $email, 'password' => $password), true)) + { + // 用户状态永久保存... + } + +**注意:** 如果 `attempt` 方法返回 `true`, 用户就已经成功登陆应用程序了。 + +#### 确定用户是否是通过“Remember”得到的授权 + +如果你在应用中“记住”了用户的登录状态,你可以使用 `viaRemember` 方法来确定用户是否正在使用“remember me”这个 cookie 来获得授权: + + if (Auth::viaRemember()) + { + // + } + +#### 指定其它条件验证用户 + +你也可以在用户验证查询中加入其它条件: + + if (Auth::attempt(array('email' => $email, 'password' => $password, 'active' => 1))) + { + // 用户激动状态,没有暂停,并且存在。 + } + +> **注意:** 为了增强安全性,防止会话固定,用户的 SESSION ID 会在每次验证后重新生成。 + +#### 查看登陆用户 + +一旦用户验证通过了,就可以查看 User 模型/记录: + + $email = Auth::user()->email; + +To retrieve the authenticated user's ID, you may use the `id` method: + + $id = Auth::id(); + +要简单使用用户ID来登陆应用程序,可以使用 `loginUsingId` 方法: + + Auth::loginUsingId(1); + +#### 验证用户但不登陆 + +`validate` 方法可以在不登录应用程序的情况下来验证用户的信息: + + if (Auth::validate($credentials)) + { + // + } + +#### 登陆用户做单次请求 + +也可以使用 `once` 方法将一个用户登录到系统中做一个单次请求。这样的方式不会使用Session或Cookie来进行状态保持。 + + if (Auth::once($credentials)) + { + // + } + +#### 应用程序中注销用户登陆状态 + + Auth::logout(); + + +## 手动登陆用户 + +如果想登陆一个存在的用户实例,只需要简单使用`login`方法并传入该实例即可: + + $user = User::find(1); + + Auth::login($user); + +这与使用 `attempt` 方法验证用户登陆是一样的。 + + + +## 保护路由 + +路由过滤器可以保证只有通过了验证的用户才能访问指定路由。Laravel 默认提供了 `auth` 过滤器,它定义在 `app/filters.php`文件。 + +#### 保护路由 + + Route::get('profile', array('before' => 'auth', function() + { + // 只有验证用户可以访问…… + })); + +### 跨站请求伪造(CSRF)保护 + +Laravel 提供便捷的方法来避免应用程序受到跨站伪造请求的攻击。 + +#### 表单中插入 CSRF Token + + + +#### 提供表单时验证 CSRF Token + + Route::post('register', array('before' => 'csrf', function() + { + return 'You gave a valid CSRF token!'; + })); + + +## HTTP 基本验证 + +HTTP基本验证可以在不设定专门的登录页面的情况下便捷的对应用程序中的用户进行验证。要使用该功能,在路由中附加 `auth.filter` 过滤器: + +#### HTTP 基本验证来保护路由 + + Route::get('profile', array('before' => 'auth.basic', function() + { + // 只有验证用户可以访问…… + })); + +默认情况下,`basic` 过滤器验证时会使用用户记录里的 `email` 字段。如果你想使用其他字段,你可以通过传递该字段名字给 `basic` 方法作为第一个参数: + + Route::filter('auth.basic', function() + { + return Auth::basic('username'); + }); + +#### 设置无状态的 HTTP 基本过滤器 + +你也可以使用 HTTP 基本验证时不将用户cookie标识写入 session,对 API 验证极其有用。要实现该功能,请定义一个返回 `onceBasic` 方法的过滤器: + + Route::filter('basic.once', function() + { + return Auth::onceBasic(); + }); + +如果你正在使用PHP FastCGI,那么HTTP 基本验证在默认情况下是不会正常工作的。应该将下面的代码加入到`.htaccess`文件中: + + RewriteCond %{HTTP:Authorization} ^(.+)$ + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + +## 密码提示和重置 + +### Model & Table + +大多web应用程序提供了让用户重置密码功能。与其让你在每个应用程序中重复去实现该功能,Laravel 提供非常方便的方法来发送密码提示以及执行密码重置。在开始前,请确认你的 `User` 模型实现了 `Illuminate\Auth\Reminders\RemindableInterface` 接口。 当然,框架中的默认的 `User` 模型已经实现了该接口。 + +#### 实现 RemindableInterface 接口 + + use Illuminate\Auth\Reminders\RemindableTrait; + use Illuminate\Auth\Reminders\RemindableInterface; + + class User extends Eloquent implements RemindableInterface { + + use RemindableTrait; + + } + +#### 为提示表建立迁移 + +接下来,要创建一个表来存储密码重置 token。要为该表生成迁移,简单执行 Artisan 命令 `auth::reminders`: + + php artisan auth:reminders-table + + php artisan migrate + +### Password Reminder Controller + +Now we're ready to generate the password reminder controller. To automatically generate a controller, you may use the `auth:reminders-controller` Artisan command, which will create a `RemindersController.php` file in your `app/controllers` directory. + + php artisan auth:reminders-controller + +The generated controller will already have a `getRemind` method that handles showing your password reminder form. All you need to do is create a `password.remind` [view](/docs/responses#views). This view should have a basic form with an `email` field. The form should POST to the `RemindersController@postRemind` action. + +A simple form on the `password.remind` view might look like this: + +
    + + +
    + +In addition to `getRemind`, the generated controller will already have a `postRemind` method that handles sending the password reminder e-mails to your users. This method expects the `email` field to be present in the `POST` variables. If the reminder e-mail is successfully sent to the user, a `status` message will be flashed to the session. If the reminder fails, an `error` message will be flashed instead. + +Within the `postRemind` controller method you may modify the message instance before it is sent to the user: + + Password::remind(Input::only('email'), function($message) + { + $message->subject('Password Reminder'); + }); + +Your user will receive an e-mail with a link that points to the `getReset` method of the controller. The password reminder token, which is used to identify a given password reminder attempt, will also be passed to the controller method. The action is already configured to return a `password.reset` view which you should build. The `token` will be passed to the view, and you should place this token in a hidden form field named `token`. In addition to the `token`, your password reset form should contain `email`, `password`, and `password_confirmation` fields. The form should POST to the `RemindersController@postReset` method. + +A simple form on the `password.reset` view might look like this: + +
    + + + + + +
    + +Finally, the `postReset` method is responsible for actually changing the password in storage. In this controller action, the Closure passed to the `Password::reset` method sets the `password` attribute on the `User` and calls the `save` method. Of course, this Closure is assuming your `User` model is an [Eloquent model](/docs/eloquent); however, you are free to change this Closure as needed to be compatible with your application's database storage system. + +If the password is successfully reset, the user will be redirected to the root of your application. Again, you are free to change this redirect URL. If the password reset fails, the user will be redirect back to the reset form, and an `error` message will be flashed to the session. + +### 密码的验证规则 + +默认情况下 `Password::reset` 方法将会验证这个密码的长度 >= 6。你可以使用 `Password::validator` 方法,在它接收的匿名函数中自定义验证规则。在这个匿名函数中你可以做任何你想要的密码验证。请注意,你不需要验证密码是否匹配,因为这将由框架自动完成。 + + Password::validator(function($credentials) + { + return strlen($credentials['password']) >= 6; + }); + +> **注意:** 默认情况下,密码重置令牌的过期时间为1小时。你可以在 `app/config/auth.php` 文件的 `reminder.expire` 配置项中进行修改。 + + +## 加密 + +Laravel通过 mcrypt PHP 的扩展提供了强大的AES 加密组件: + +#### 加密 + + $encrypted = Crypt::encrypt('secret'); + +> **注意:** 确认在 `app/config/app.php` 文件设置了一个16、24或32随机字符给 `key` 项。否则,加密的值是不安全的。 + +#### 解密 + + $decrypted = Crypt::decrypt($encryptedValue); + +#### 设置 Cipher 和 Mode + +你也可以设置在encrypter中使用的 cipher 和 mode: + + Crypt::setMode('crt'); + + Crypt::setCipher($cipher); + + +## Authentication Drivers + +Laravel offers the `database` and `eloquent` authentication drivers out of the box. For more information about adding additional authentication drivers, check out the [Authentication extension documentation](/docs/extending#authentication). diff --git a/cn/session.md b/cn/session.md new file mode 100644 index 0000000..a9ea361 --- /dev/null +++ b/cn/session.md @@ -0,0 +1,114 @@ +# Session + +- [配置](#configuration) +- [Session 用法](#session-usage) +- [闪存数据](#flash-data) +- [数据库 Sessions](#database-sessions) +- [Session 启动](#session-drivers) + + +## 配置 + +因为HTTP协议本身是无状态的,session提供了一种保存用户请求信息的途径。Laravel框架可以使用多种session后端驱动,并且提供了清晰、统一的API支持。框架内置支持一些比较流行的后端驱动如[Memcached](http://memcached.org)、 [Redis](http://redis.io)和数据库。 + +session的配置被存放在 `app/config/session.php` 文件中。请务必查看一下这个文件中那些带有注释的配置选项。Laravel默认使用`基于文件(file)`的session驱动,它可以在大多数应用中良好地工作。 + +#### Reserved Keys + +The Laravel framework uses the `flash` session key internally, so you should not add an item to the session by that name. + + +## Session 用法 + +#### 储存一个Session变量 + + Session::put('key', 'value'); + +#### Push A Value Onto An Array Session Value + + Session::push('user.teams', 'developers'); + +#### 读取一个Session变量 + + $value = Session::get('key'); + +#### 读取一个Session变量或者返回默认值 + + $value = Session::get('key', 'default'); + + $value = Session::get('key', function() { return 'default'; }); + +#### 读取一个变量并遗忘它 + + $value = Session::pull('key', 'default'); + +#### 读取所有Session数据 + + $data = Session::all(); + +#### 检查一个Session变量是否存在 + + if (Session::has('users')) + { + // + } + +#### 删除一个Session变量 + + Session::forget('key'); + +#### 删除所有Session变量 + + Session::flush(); + +#### 重置 Session ID + + Session::regenerate(); + + +## 闪存数据 + +有时,你也许希望将信息暂存在session中到下一次请求为止,你可以使用 `Session::flash` 方法达到这个目的: + + Session::flash('key', 'value'); + +#### 刷新当前闪存数据,延长其过期时间到下一个请求 + + Session::reflash(); + +#### 刷新一部分闪存数据 + + Session::keep(array('username', 'email')); + + +## 数据库 Sessions + +当使用 `数据库(database)` session驱动时,你需要设置一张存储session数据的表。下面的例子展示了使用 `Schema` 声明新建一张表: + + Schema::create('sessions', function($table) + { + $table->string('id')->unique(); + $table->text('payload'); + $table->integer('last_activity'); + }); + +当然,你也可以使用Artisan命令 `session:table` 来创建这张表: + + php artisan session:table + + composer dump-autoload + + php artisan migrate + + +## Session Drivers + +The session "driver" defines where session data will be stored for each request. Laravel ships with several great drivers out of the box: + +- `file` - sessions will be stored in `app/storage/sessions`. +- `cookie` - sessions will be stored in secure, encrypted cookies. +- `database` - sessions will be stored in a database used by your application. +- `memcached` / `redis` - sessions will be stored in one of these fast, cached based stores. +- `array` - sessions will be stored in a simple PHP array and will not be persisted across requests. + +> **Note:** The array driver is typically used for running [unit tests](/docs/testing), so no session data will be persisted. \ No newline at end of file diff --git a/cn/ssh.md b/cn/ssh.md new file mode 100644 index 0000000..33bad46 --- /dev/null +++ b/cn/ssh.md @@ -0,0 +1,263 @@ +# SSH(Secure Shell) + +- [配置](#configuration) +- [基本用法](#basic-usage) +- [任务](#tasks) +- [SFTP 下载](#sftp-downloads) +- [SFTP上传](#sftp-uploads) +- [显示远程日志的末尾几行](#tailing-remote-logs) +- [Envoy Task Runner](#envoy-task-runner) + + +## 配置 + +Laravel 为 SSH 登录远程服务器并运行命令提供了一种简单的方式,允许你轻松创建运行在远程服务器上的 Artisan 任务。`SSH` 门面类(facade)提供了连接远程服务器和运行命令的访问入口。 + +配置文件位于 `app/config/remote.php`,该文件包含了配置远程连接所需的所有选项。`connections` 数组包含了一个以名称作为键的服务器列表。只需将 `connections` 数组中的凭证信息填好,就可以开始运行远程任务了。注意 `SSH` 可以使用密码或 SSH 密钥进行身份验证。 + +> **Note:** Need to easily run a variety of tasks on your remote server? Check out the [Envoy task runner](#envoy-task-runner)! + + +## 基本用法 + +#### 在默认服务器上运行命令 + +使用 `SSH::run` 方法在 `default` 远程服务器连接上运行命令: + + SSH::run(array( + 'cd /var/www', + 'git pull origin master', + )); + +#### 在指定连接上运行命令 + +或者,你可以使用 `into` 方法在指定的服务器连接上运行命令: + + SSH::into('staging')->run(array( + 'cd /var/www', + 'git pull origin master', + )); + +#### 捕获命令输出结果 + +你可以通过向 `run` 方法传递一个闭包来捕获远程命令的“实时”输出结果: + + SSH::run($commands, function($line) + { + echo $line.PHP_EOL; + }); + +## 任务 + + +如果需要定义一组经常放在一起运行的命令,你可以使用 `define` 方法来定义一个任务( `task` ): + + SSH::into('staging')->define('deploy', array( + 'cd /var/www', + 'git pull origin master', + 'php artisan migrate', + )); + +任务一旦创建,就可以使用 `task` 来执行: + + SSH::into('staging')->task('deploy', function($line) + { + echo $line.PHP_EOL; + }); + + +## SFTP Downloads + +The `SSH` class includes a simple way to download files using the `get` and `getString` methods: + + SSH::into('staging')->get($remotePath, $localPath); + + $contents = SSH::into('staging')->getString($remotePath); + + +## SFTP 上传 + +`SSH` 类的 `put` 和 `putString` 方法提供了一种简单的上传方式,用于将文件或字符串上传到服务器: + + SSH::into('staging')->put($localFile, $remotePath); + + SSH::into('staging')->putString($remotePath, 'Foo'); + + +## 显示远程日志的末尾几行 + +Laravel 提供了一个有用的命令用来查看任何远程连接服务器上的 `laravel.log` 文件的末尾几行内容。只需使用 Artisan 命令 `tail` 并指定你想要查看日志的远程连接的名称即可: + + php artisan tail staging + + php artisan tail staging --path=/path/to/log.file + + +## Envoy Task Runner + +- [Installation](#envoy-installation) +- [Running Tasks](#envoy-running-tasks) +- [Multiple Servers](#envoy-multiple-servers) +- [Parallel Execution](#envoy-parallel-execution) +- [Task Macros](#envoy-task-macros) +- [Notifications](#envoy-notifications) +- [Updating Envoy](#envoy-updating-envoy) + +Laravel Envoy provides a clean, minimal syntax for defining common tasks you run on your remote servers. Using a [Blade](/docs/templates#blade-templating) style syntax, you can easily setup tasks for deployment, Artisan commands, and more. + +> **Note:** Envoy requires PHP version 5.4 or greater, and only runs on Mac / Linux operating systems. + + +### Installation + +First, install Envoy using the Composer `global` command: + + composer global require "laravel/envoy=~1.0" + +Make sure to place the `~/.composer/vendor/bin` directory in your PATH so the `envoy` executable is found when you run the `envoy` command in your terminal. + +Next, create an `Envoy.blade.php` file in the root of your project. Here's an example to get you started: + + @servers(['web' => '192.168.1.1']) + + @task('foo', ['on' => 'web']) + ls -la + @endtask + +As you can see, an array of `@servers` is defined at the top of the file. You can reference these servers in the `on` option of your task declarations. Within your `@task` declarations you should place the Bash code that will be run on your server when the task is executed. + +The `init` command may be used to easily create a stub Envoy file: + + envoy init user@192.168.1.1 + + +### Running Tasks + +To run a task, use the `run` command of your Envoy installation: + + envoy run foo + +If needed, you may pass variables into the Envoy file using command line switches: + + envoy run deploy --branch=master + +You may use the options via the Blade syntax you are used to: + + @servers(['web' => '192.168.1.1']) + + @task('deploy', ['on' => 'web']) + cd site + git pull origin {{ $branch }} + php artisan migrate + @endtask + +#### Bootstrapping + +You may use the ```@setup``` directive to declare variables and do general PHP work inside the Envoy file: + + @setup + $now = new DateTime(); + + $environment = isset($env) ? $env : "testing"; + @endsetup + +You may also use ```@include``` to include any PHP files: + + @include('vendor/autoload.php'); + + +### Multiple Servers + +You may easily run a task across multiple servers. Simply list the servers in the task declaration: + + @servers(['web-1' => '192.168.1.1', 'web-2' => '192.168.1.2']) + + @task('deploy', ['on' => ['web-1', 'web-2']]) + cd site + git pull origin {{ $branch }} + php artisan migrate + @endtask + +By default, the task will be executed on each server serially. Meaning, the task will finish running on the first server before proceeding to execute on the next server. + + +### Parallel Execution + +If you would like to run a task across multiple servers in parallel, simply add the `parallel` option to your task declaration: + + @servers(['web-1' => '192.168.1.1', 'web-2' => '192.168.1.2']) + + @task('deploy', ['on' => ['web-1', 'web-2'], 'parallel' => true]) + cd site + git pull origin {{ $branch }} + php artisan migrate + @endtask + + +### Task Macros + +Macros allow you to define a set of tasks to be run in sequence using a single command. For instance: + + @servers(['web' => '192.168.1.1']) + + @macro('deploy') + foo + bar + @endmacro + + @task('foo') + echo "HELLO" + @endtask + + @task('bar') + echo "WORLD" + @endtask + +The `deploy` macro can now be run via a single, simple command: + + envoy run deploy + + + +### Notifications + +#### HipChat + +After running a task, you may send a notification to your team's HipChat room using the simple `@hipchat` directive: + + @servers(['web' => '192.168.1.1']) + + @task('foo', ['on' => 'web']) + ls -la + @endtask + + @after + @hipchat('token', 'room', 'Envoy') + @endafter + +You can also specify a custom message to the hipchat room. Any variables declared in ```@setup``` or included with ```@include``` will be available for use in the message: + + @after + @hipchat('token', 'room', 'Envoy', "$task ran on [$environment]") + @endafter + +This is an amazingly simple way to keep your team notified of the tasks being run on the server. + +#### Slack + +The following syntax may be used to send a notification to [Slack](https://slack.com): + + @after + @slack('team', 'token', 'channel') + @endafter + + +### Updating Envoy + +To update Envoy, simply run the `self-update` command: + + envoy self-update + +If your Envoy installation is in `/usr/local/bin`, you may need to use `sudo`: + + sudo envoy self-update diff --git a/cn/templates.md b/cn/templates.md new file mode 100644 index 0000000..1ff928b --- /dev/null +++ b/cn/templates.md @@ -0,0 +1,182 @@ +# 模板 + +- [控制器布局](#controller-layouts) +- [Blade模板](#blade-templating) +- [其他 Blade模板 控制结构](#other-blade-control-structures) +- [Extending Blade](#extending-blade) + + +## 控制器布局 + +在Laravel框架中使用模板的一种方法就是通过控制器布局。通过在控制器中指定 `layout` 属性,对应的视图会被创建并且作为请求的默认返回数据。 + +#### 在控制器中定义一个布局 + + class UserController extends BaseController { + + /** + * The layout that should be used for responses. + */ + protected $layout = 'layouts.master'; + + /** + * Show the user profile. + */ + public function showProfile() + { + $this->layout->content = View::make('user.profile'); + } + + } + + +## Blade模板 + +Blade是Laravel框架下的一种简单又强大的模板引擎。 +不同于控制器布局,Blade模板引擎由模板继承和模板片段驱动。所有的Blade模板文件必须使用Blade `.blade.php` 文件扩展名。 + + +#### 定义一个Blade布局 + + + + + + @section('sidebar') + This is the master sidebar. + @show + +
    + @yield('content') +
    + + + +#### 使用一个Blade布局 + + @extends('layouts.master') + + @section('sidebar') + @parent + +

    This is appended to the master sidebar.

    + @stop + + @section('content') +

    This is my body content.

    + @stop + +注意一个Blade布局的扩展视图简单地在布局中替换了模板片段。通过在模板片段中使用 `@parent` 指令,布局的内容可以被包含在一个子视图中,这样你就可以在布局片段中添加诸如侧边栏、底部信息的内容。 + +Sometimes, such as when you are not sure if a section has been defined, you may wish to pass a default value to the `@yield` directive. You may pass the default value as the second argument: + + @yield('section', 'Default Content'); + + +## 其他 Blade模板 控制结构 + +#### 输出数据 + + Hello, {{{ $name }}}. + + The current UNIX timestamp is {{{ time() }}}. + +#### Echoing Data After Checking For Existence + +Sometimes you may wish to echo a variable, but you aren't sure if the variable has been set. Basically, you want to do this: + + {{{ isset($name) ? $name : 'Default' }}} + +However, instead of writing a ternary statement, Blade allows you to use the following convenient short-cut: + + {{{ $name or 'Default' }}} + +#### Displaying Raw Text With Curly Braces + +If you need to display a string that is wrapped in curly braces, you may escape the Blade behavior by prefixing your text with an `@` symbol: + + @{{ This will not be processed by Blade }} + +Of course, all user supplied data should be escaped or purified. To escape the output, you may use the triple curly brace syntax: + + Hello, {{{ $name }}}. + +If you don't want the data to be escaped, you may use double curly-braces: + + Hello, {{ $name }}. + +> **Note:** Be very careful when echoing content that is supplied by users of your application. Always use the triple curly brace syntax to escape any HTML entities in the content. + +#### If 声明 + + @if (count($records) === 1) + I have one record! + @elseif (count($records) > 1) + I have multiple records! + @else + I don't have any records! + @endif + + @unless (Auth::check()) + You are not signed in. + @endunless + +#### 循环 + + @for ($i = 0; $i < 10; $i++) + The current value is {{ $i }} + @endfor + + @foreach ($users as $user) +

    This is user {{ $user->id }}

    + @endforeach + + @while (true) +

    I'm looping forever.

    + @endwhile + +#### 包含子视图 + + @include('view.name') + +You may also pass an array of data to the included view: + + @include('view.name', array('some'=>'data')) + +#### Overwriting Sections + +By default, sections are appended to any previous content that exists in the section. To overwrite a section entirely, you may use the `overwrite` statement: + + @extends('list.item.container') + + @section('list.item.content') +

    This is an item of type {{ $item->type }}

    + @overwrite + +#### 输出多语言(Language Lines) + + @lang('language.line') + + @choice('language.line', 1); + +#### 注释 + + {{-- This comment will not be in the rendered HTML --}} + + +## Extending Blade + +Blade even allows you to define your own custom control structures. When a Blade file is compiled, each custom extension is called with the view contents, allowing you to do anything from simple `str_replace` manipulations to more complex regular expressions. + +The Blade compiler comes with the helper methods `createMatcher` and `createPlainMatcher`, which generate the expression you need to build your own custom directives. + +The `createPlainMatcher` method is used for directives with no arguments like `@endif` and `@stop`, while `createMatcher` is used for directives with arguments. + +The following example creates a `@datetime($var)` directive which simply calls `->format()` on `$var`: + + Blade::extend(function($view, $compiler) + { + $pattern = $compiler->createMatcher('datetime'); + + return preg_replace($pattern, '$1format('m/d/Y H:i'); ?>', $view); + }); diff --git a/cn/testing.md b/cn/testing.md new file mode 100644 index 0000000..cbabfce --- /dev/null +++ b/cn/testing.md @@ -0,0 +1,221 @@ +# 单元测试 + +- [简介](#introduction) +- [定义 & 运行测试](#defining-and-running-tests) +- [测试环境](#test-environment) +- [测试中执行路由](#calling-routes-from-tests) +- [模拟Facades](#mocking-facades) +- [框架断言](#framework-assertions) +- [辅助函数](#helper-methods) +- [Refreshing The Application](#refreshing-the-application) + + +## 简介 + +Laravel 自建了单元测试。事实上,我们支持使用自带的 PHPUnit 进行测试,并且已经为你的应用程序配置好了 `phpunit.xml` 文件。除了 PHPUnit,Laravel 也使用了 Symfony HttpKernel,DomCrawler 和 BrowserKit 组件,允许你在测试中检查和操作视图以及模拟浏览器行为。 + +在 `app/tests` 目录下已经提供了一个测试示例文件。在安装了一个全新的 Laravel 应用程序之后,只需要简单的在命令行中执行 `phpunit`, 即可运行你的测试。 + + + +## 定义 & 运行测试 + +要创建一个测试用例,只需要简单的在 `app/tests` 目录创建一个新的测试文件。测试类需要继承 `TestCase` 。然后你可以像使用 PHPUnit 一样来定义这些测试函数。 + +#### 一个测试类示例 + + class FooTest extends TestCase { + + public function testSomethingIsTrue() + { + $this->assertTrue(true); + } + + } + +你可以在终端执行 `phpunit` 命令来运行应用程序中的所有测试。 + +> **注意:** 如果你定义了自己的 `setUp` 函数,请确保调用了 `parent::setUp `。 + + +## 测试环境 + +在运行单元测试时,Laravel 将自动设置环境配置为 `testing` 。并且,Laravel 在测试环境中包含了 `session` 和 `cache` 配置文件。测试环境中,这两个驱动都被设置为空 `array`,意味着在测试过程中,不会将 session 或者 cache 持久化。必要时,你也可以创建其他的测试环境。 + + +## 测试中执行路由 + +#### 测试中执行路由 + +在测试时可以很简单的使用 `call` 函数来执行一个路由: + + $response = $this->call('GET', 'user/profile'); + + $response = $this->call($method, $uri, $parameters, $files, $server, $content); + +你可以检查 `Illuminate\Http\Response` 对象: + + $this->assertEquals('Hello World', $response->getContent()); + +#### 测试中执行控制器 + +你也可以在测试中执行控制器: + + $response = $this->action('GET', 'HomeController@index'); + + $response = $this->action('GET', 'UserController@profile', array('user' => 1)); + +`getContent` 函数将会返回和响应相同的字符串内容。如果路由返回 `视图(View)` , 你可以使用 `original` 属性来访问: + + $view = $response->original; + + $this->assertEquals('John', $view['name']); + +要执行一个 HTTPS 路由,可以使用 `callSecure` 函数: + + $response = $this->callSecure('GET', 'foo/bar'); + +> **注意:** 在测试环境中,路由过滤器将被禁用。如果需要启用它们, 请在你的测试中添加 `Route::enableFilters()` 。 + +### DOM Crawler + +你也可以执行一个路由并且接受一个 DOM Crawler 实例来检查内容: + + $crawler = $this->client->request('GET', '/'); + + $this->assertTrue($this->client->getResponse()->isOk()); + + $this->assertCount(1, $crawler->filter('h1:contains("Hello World!")')); + +如需获取更多关于 DOM Crawler 的信息,请参见文档 [official documentation](http://symfony.com/doc/master/components/dom_crawler.html)。 + + +## 模拟Facades + +在测试时,你可能经常需要模拟执行一个 Laravel 静态 facade。例如,参考如下控制器的动作: + + public function getIndex() + { + Event::fire('foo', array('name' => 'Dayle')); + + return 'All done!'; + } + +我们可以通过在 facade 使用 `shouldReceive` 函数来模拟执行 `Event` 类, 它将返回一个 [Mockery](https://github.com/padraic/mockery) 模拟的实例。 + + + +#### 模拟一个Facade + + public function testGetIndex() + { + Event::shouldReceive('fire')->once()->with('foo', array('name' => 'Dayle')); + + $this->call('GET', '/'); + } + +> **注意:** 你不能模拟 `Request` 的 facade。替代的方式为,在运行你的测试时,将你预期的输入传给 `call` 函数。 + + +## 框架断言 + +Laravel 提供了一些 `断言(assert)` 函数来让测试更加容易: + +#### 断言响应为OK + + public function testMethod() + { + $this->call('GET', '/'); + + $this->assertResponseOk(); + } + +#### 断言响应状态码 + + $this->assertResponseStatus(403); + +#### 断言响应为重定向 + + $this->assertRedirectedTo('foo'); + + $this->assertRedirectedToRoute('route.name'); + + $this->assertRedirectedToAction('Controller@method'); + +#### 断言视图含有的一些数据 + + public function testMethod() + { + $this->call('GET', '/'); + + $this->assertViewHas('name'); + $this->assertViewHas('age', $value); + } + +#### 断言 Session 中含有的一些数据 + + public function testMethod() + { + $this->call('GET', '/'); + + $this->assertSessionHas('name'); + $this->assertSessionHas('age', $value); + } + +#### Asserting The Session Has Errors + + public function testMethod() + { + $this->call('GET', '/'); + + $this->assertSessionHasErrors(); + + // Asserting the session has errors for a given key... + $this->assertSessionHasErrors('name'); + + // Asserting the session has errors for several keys... + $this->assertSessionHasErrors(array('name', 'age')); + } + +#### Asserting Old Input Has Some Data + + public function testMethod() + { + $this->call('GET', '/'); + + $this->assertHasOldInput(); + } + + +## 辅助函数 + +`TestCase` 类包含一些辅助函数来让应用程序测试更加容易。 + +#### Setting And Flushing Sessions From Tests + + $this->session(['foo' => 'bar']); + + $this->flushSession(); + +#### 设置当前通过验证的用户 + +你可以使用 `be` 函数来设置当前通过验证的用户: + + $user = new User(array('name' => 'John')); + + $this->be($user); + +你可以在测试中使用 `seed` 函数来重新填充你的数据库: + +#### 测试中重新填充数据库 + + $this->seed(); + + $this->seed($connection); + +如需获取更多关于创建数据填充的信息,请参见文档 [迁移 & 数据填充](/docs/migrations#database-seeding)。 + + +## Refreshing The Application + +As you may already know, you can access your Laravel `Application` / IoC Container via `$this->app` from any test method. This Application instance is refreshed for each test class. If you wish to manually force the Application to be refreshed for a given method, you may use the `refreshApplication` method from your test method. This will reset any extra bindings, such as mocks, that have been placed in the IoC container since the test case started running. diff --git a/cn/validation.md b/cn/validation.md new file mode 100644 index 0000000..a458029 --- /dev/null +++ b/cn/validation.md @@ -0,0 +1,559 @@ +# 验证 + +- [基本使用](#basic-usage) +- [使用错误消息](#working-with-error-messages) +- [错误消息 & 视图](#error-messages-and-views) +- [可用的验证规则](#available-validation-rules) +- [有条件的添加规则](#conditionally-adding-rules) +- [自定义错误消息](#custom-error-messages) +- [自定义验证规则](#custom-validation-rules) + + +## 基本使用 + +Laravel 自带一个简单、方便的 `Validation` 类用于验证数据以及获取错误消息。 + +#### 基本验证例子 + + $validator = Validator::make( + array('name' => 'Dayle'), + + array('name' => 'required|min:5') + ); + +传递给 `make` 函数的第一个参数是待验证的数据,第二个参数是对该数据需要应用的验证规则。 + +#### 通过数组指定验证规则 + +多个验证规则可以通过 "|" 字符进行分隔,或者作为数组的一个单独的元素。 + + $validator = Validator::make( + array('name' => 'Dayle'), + array('name' => array('required', 'min:5')) + ); + +#### 验证多个字段 + + $validator = Validator::make( + array( + 'name' => 'Dayle', + 'password' => 'lamepassword', + 'email' => 'email@example.com' + ), + array( + 'name' => 'required', + 'password' => 'required|min:8', + 'email' => 'required|email|unique:users' + ) + ); + +一旦一个 `Validator` 实例被创建,可以使用 `fails` (或者 `passes`)函数执行这个验证。 + + if ($validator->fails()) + { + // The given data did not pass validation + } + +如果验证失败,你可以从验证器中获取错误消息。 + + $messages = $validator->messages(); + +你也可以使用 `failed` 函数得到不带错误消息的没有通过验证的规则的数组。 + + $failed = $validator->failed(); + +#### 文件验证 + +`Validator` 类提供了一些验证规则用于验证文件,比如 `size`、`mimes`等。在验证文件的时候,你可以和其他数据一起传递给验证器。 + + +## 使用错误消息 + +在一个 `Validator` 实例上调用 `messages` 函数之后,将会得到一个 `MessageBag` 实例,该实例拥有很多方便使用错误消息的函数。 + +#### 获取一个字段的第一个错误消息 + + echo $messages->first('email'); + +#### 获取一个字段的全部错误消息** + + foreach ($messages->get('email') as $message) + { + // + } + +#### 获取全部字段的全部错误消息 + + foreach ($messages->all() as $message) + { + // + } + +#### 检查一个字段是否存在消息 + + if ($messages->has('email')) + { + // + } + +#### 以某种格式获取一条错误消息 + + echo $messages->first('email', '

    :message

    '); + +> **注意:** 默认情况下,消息将使用与 Bootstrap 兼容的语法进行格式化。 + +#### 以某种格式获取所有错误消息 + + foreach ($messages->all('
  • :message
  • ') as $message) + { + // + } + + +## 错误消息 & 视图 + +一旦你执行了验证,你需要一种简单的方法向视图反馈错误消息。这在 Lavavel 中能够方便的处理。以下面的路由作为例: + + Route::get('register', function() + { + return View::make('user.register'); + }); + + Route::post('register', function() + { + $rules = array(...); + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) + { + return Redirect::to('register')->withErrors($validator); + } + }); + +注意当验证失败,我们使用 `withErrors` 函数把 `Validator` 实例传递给 Redirect。这个函数将刷新 Session 中保存的错误消息,使得它们在下次请求中可用。 + +然而,请注意在我们的 GET 路由中并没有明确的绑定错误消息到视图。这是因为 Laravel 总会检查 Session 中的错误,并且如果它们是可用的将自动绑定到视图。**所以,需要特别注意的是,对于每个请求,一个 `$errors` 变量在所有视图中总是可用的**,允许你方便的认为 `$errors` 变量总是被定义并可以安全使用的。`$errors` 变量将是一个 `MessageBag` 类的实例。 + +所以,在跳转之后,你可以在视图中使用自动绑定的 `$errors` 变量: + + first('email'); ?> + +### Named Error Bags + +If you have multiple forms on a single page, you may wish to name the `MessageBag` of errors. This will allow you to retrieve the error messages for a specific form. Simply pass a name as the second argument to `withErrors`: + + return Redirect::to('register')->withErrors($validator, 'login'); + +You may then access the named `MessageBag` instance from the `$errors` variable: + + login->first('email'); ?> + + +## 可用的验证规则 + +下面是一个所有可用的验证规则的列表以及它们的功能: + +- [Accepted](#rule-accepted) +- [Active URL](#rule-active-url) +- [After (Date)](#rule-after) +- [Alpha](#rule-alpha) +- [Alpha Dash](#rule-alpha-dash) +- [Alpha Numeric](#rule-alpha-num) +- [Array](#rule-array) +- [Before (Date)](#rule-before) +- [Between](#rule-between) +- [Confirmed](#rule-confirmed) +- [Date](#rule-date) +- [Date Format](#rule-date-format) +- [Different](#rule-different) +- [Digits](#rule-digits) +- [Digits Between](#rule-digits-between) +- [E-Mail](#rule-email) +- [Exists (Database)](#rule-exists) +- [Image (File)](#rule-image) +- [In](#rule-in) +- [Integer](#rule-integer) +- [IP Address](#rule-ip) +- [Max](#rule-max) +- [MIME Types](#rule-mimes) +- [Min](#rule-min) +- [Not In](#rule-not-in) +- [Numeric](#rule-numeric) +- [Regular Expression](#rule-regex) +- [Required](#rule-required) +- [Required If](#rule-required-if) +- [Required With](#rule-required-with) +- [Required With All](#rule-required-with-all) +- [Required Without](#rule-required-without) +- [Required Without All](#rule-required-without-all) +- [Same](#rule-same) +- [Size](#rule-size) +- [Unique (Database)](#rule-unique) +- [URL](#rule-url) + + +#### accepted + +验证此规则的值必须是 _yes_、 _on_ 或者是 _1_。这在验证是否同意"服务条款"的时候非常有用。 + + +#### active_url + +验证此规则的值必须是一个合法的 URL,根据 PHP 函数 `checkdnsrr`。 + + +#### after:_date_ + +验证此规则的值必须在给定日期之后,该日期将被传递到 PHP 的 `strtotime` 函数。 + + +#### alpha + +验证此规则的值必须全部由字母字符构成。 + + +#### alpha_dash + +验证此规则的值必须全部由字母、数字、中划线或下划线字符构成。 + + +#### alpha_num + +验证此规则的值必须全部由字母和数字构成。 + + +#### array + +The field under validation must be of type array. + + +#### before:_date_ + +验证此规则的值必须在给定日期之前,该日期将被传递到 PHP 的 `strtotime` 函数。 + + +#### between:_min_,_max_ + +验证此规则的值必须在给定的 _min_ 和 _max_ 之间。字符串、数字以及文件都将使用大小规则进行比较。 + + +#### confirmed + +验证此规则的值必须和 `foo_confirmation` 的值相同。比如,需要验证此规则的字段是 `password`,那么在输入中必须有一个与之相同的 `password_confirmation` 字段。 + + +#### date + +验证此规则的值必须是一个合法的日期,根据 PHP 函数 `strtotime`。 + + +#### date_format:_format_ + +验证此规则的值必须符合给定的 _format_ 的格式,根据 PHP 函数 `date_parse_from_format`。 + + +#### different:_field_ + +验证此规则的值必须与指定的 _field_ 字段的值不同。 + + +#### digits:_value_ + +验证此规则的值必须是一个 _数字_ 并且必须满足 _value_ 设定的精确长度。 + + +#### digits_between:_min_,_max_ + +验证此规则的值,它的长度必须介于 _min_ 和 _max_ 之间。 + + +#### email + +验证此规则的值必须是一个合法的电子邮件地址。 + + +#### exists:_table_,_column_ + +验证此规则的值必须在指定的数据库的表中存在。 + +#### Exists 规则的基础使用 + + 'state' => 'exists:states' + +#### 指定列名 + + 'state' => 'exists:states,abbreviation' + +你也可以指定更多的条件,将以 "where" 的形式添加到查询。 + + 'email' => 'exists:staff,email,account_id,1' + +传递 `NULL` 到 "where" 子句中,将会直接在数据库中查找 `NULL` 值: + + 'email' => 'exists:staff,email,deleted_at,NULL' + + +#### image + +验证此规则的值必须是一个图片 (jpeg, png, bmp 或者 gif)。 + + +#### in:_foo_,_bar_,... + +验证此规则的值必须在给定的列表中存在。 + + +#### integer + +验证此规则的值必须是一个整数。 + + +#### ip + +验证此规则的值必须是一个合法的 IP 地址。 + + +#### max:_value_ + +验证此规则的值必须小于等于最大值 _value_。字符串、数字以及文件都将使用大小规则进行比较。 + + +#### mimes:_foo_,_bar_,... + +验证此规则的文件的 MIME 类型必须在给定的列表中。 + +#### MIME 规则的基础使用 + + 'photo' => 'mimes:jpeg,bmp,png' + + +#### min:_value_ + +验证此规则的值必须大于最小值 _value_。字符串、数字以及文件都将使用大小规则进行比较。 + + +#### not_in:_foo_,_bar_,... + +验证此规则的值必须在给定的列表中不存在。 + + +#### numeric + +验证此规则的值必须是一个数字。 + + +#### regex:_pattern_ + +验证此规则的值必须符合给定的正则表达式。 + +**注意:** 当使用 `regex` 模式的时候,可能需要在一个数组中指定规则,而不是使用 "|" 分隔符,特别是正则表达式中包含一个 "|" 字符的时候。 + + +#### required + +验证此规则的值必须在输入数据中存在。 + + +#### required\_if:_field_,_value_ + +如果指定的 _field_ 字段等于指定的 _value_ ,那么验证此规则的值必须存在。 + + +#### required_with:_foo_,_bar_,... + +_仅当_ 任意其它指定的字段存在的时候,验证此规则的值必须存在。 + + +#### required_with_all:_foo_,_bar_,... + +_仅当_ 所有其它指定的字段存在的时候,验证此规则的值必须存在。 + + +#### required_without:_foo_,_bar_,... + +_仅当_ 其它指定的字段有一个不存在的时候,验证此规则的值必须存在。 + + +#### required_without_all:_foo_,_bar_,... + +_仅当_ 其它指定的字段都不存在的时候,验证此规则的值必须存在。 + + +#### same:_field_ + +验证此规则的值必须与给定的 _field_ 字段的值相同。 + + +#### size:_value_ + +验证此规则的值的大小必须与给定的 _value_ 相同。对于字符串,_value_ 代表字符的个数;对于数字,_value_ 代表它的整数值,对于文件,_value_ 代表文件以KB为单位的大小。 + + +#### unique:_table_,_column_,_except_,_idColumn_ + +验证此规则的值必须在给定的数据库的表中唯一。如果 `column` 没有被指定,将使用该字段的名字。 + +#### Unique 规则的基础使用 + + 'email' => 'unique:users' + +#### 指定列名 + + 'email' => 'unique:users,email_address' + +#### 强制忽略一个给定的 ID + + 'email' => 'unique:users,email_address,10' + +#### 添加额外的where语句 + +你还可以指定更多条件,这些条件将被添加到查询的"where"语句中: + + 'email' => 'unique:users,email_address,NULL,id,account_id,1' + +在上面的规则中,只有`account_id` 为 `1` 的行才会被包含到unique检查中。 + + +#### url + +验证此规则的值必须是一个合法的 URL。 + +> **Note:** This function uses PHP's `filter_var` method. + + +## 有条件的添加规则 + +In some situations, you may wish to run validation checks against a field **only** if that field is present in the input array. To quickly accomplish this, add the `sometimes` rule to your rule list: + + $v = Validator::make($data, array( + 'email' => 'sometimes|required|email', + )); + +In the example above, the `email` field will only be validated if it is present in the `$data` array. + +#### Complex Conditional Validation + +有时你可能希望给定的字段仅当另一个字段的值大于100的时候必须存在。或者你可能需要两个字段均含有一个给定的值,仅当另一个字段存在的时候。添加这些验证规则并没有那么麻烦。首先,创建一个使用你永远不会改变的 _static rules_ 的 `Validator` 实例: + + $v = Validator::make($data, array( + 'email' => 'required|email', + 'games' => 'required|numeric', + )); + +假设我们的WEB应用程序是服务于游戏收藏爱好者们。如果一个游戏收藏爱好者注册了我们的应用程序,并且他们拥有100多款游戏,我们想让他们说明为什么他们会拥有如此多的游戏。例如,或许他们要开一个游戏转售店,或者也许他们只是喜欢收集。为了有条件的添加这个需求,我们可以使用 `Validator` 实例的 `sometimes` 函数。 + + $v->sometimes('reason', 'required|max:500', function($input) + { + return $input->games >= 100; + }); + +`sometimes` 函数的第一个参数是我们有条件的验证的字段名。第二个参数是我们要添加的规则。如果 `Closure` 作为第三个参数且返回了 `true`,规则将被添加。这种方法可以很容易构建复杂的条件验证。你甚至可以一次性为多个字段添加条件验证: + + $v->sometimes(array('reason', 'cost'), 'required', function($input) + { + return $input->games >= 100; + }); + +> **注意:** 传递到你的 `Closure` 中的 `$input` 参数将被作为 `Illuminate\Support\Fluent` 的一个实例,并且可能被用作一个对象来访问你的输入和文件。 + + +## 自定义错误消息 + +如果有需要,你可以使用自定义的错误消息代替默认的消息。这里有好几种自定义错误消息的方法。 + +#### 传递自定义消息到验证器 + + $messages = array( + 'required' => 'The :attribute field is required.', + ); + + $validator = Validator::make($input, $rules, $messages); + +> 注意:* `:attribute` 占位符将被实际要验证的字段名替换,你也可以在错误消息中使用其他占位符。 + +#### 其他验证占位符 + + $messages = array( + 'same' => 'The :attribute and :other must match.', + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute must be between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', + ); + +#### 对一个指定的字段指定自定义的错误消息 + +有些时候,你可能希望只对一个指定的字段指定自定义的错误消息: + + $messages = array( + 'email.required' => 'We need to know your e-mail address!', + ); + + +#### 在语言文件中指定错误消息 + +在一些情况下,你可能希望在一个语言文件中指定你的错误消息而不是直接传递给 `Validator`。为了实现这个目的,请在 `app/lang/xx/validation.php` 文件中添加你的自定义消息到 `custom` 数组。 + + 'custom' => array( + 'email' => array( + 'required' => 'We need to know your e-mail address!', + ), + ), + + +## 自定义验证规则 + +#### 注册一个自定义的验证规则 + +Laravel 提供了一系列有用的验证规则;但是,你可能希望添加自己的验证规则。其中一种方法是使用 `Validator::extend` 函数注册自定义的验证规则: + + Validator::extend('foo', function($attribute, $value, $parameters) + { + return $value == 'foo'; + }); + +自定义的验证器接受三个参数:待验证属性的名字、待验证属性的值以及传递给这个规则的参数。 + +你也可以传递一个类的函数到 `extend` 函数,而不是使用闭包: + + Validator::extend('foo', 'FooValidator@validate'); + +注意你需要为你的自定义规则定义错误消息。你既可以使用一个行内的自定义消息数组,也可以在验证语言文件中进行添加。 + +#### 扩展验证器类 + +你也可以扩展 `Validator` 类本身,而不是使用闭包回调扩展验证器。为了实现这个目的,添加一个继承自 `Illuminate\Validation\Validator` 的验证器类。你可以在类中添加以 `validate` 开头的验证函数: + +