From 62b9306f5dd86578d7e84f57adf96648994a1682 Mon Sep 17 00:00:00 2001 From: Michael Grech Date: Sat, 5 Nov 2011 01:44:50 -0400 Subject: [PATCH 001/232] Corrected file name. --- start/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start/config.md b/start/config.md index aef0144..abae3af 100644 --- a/start/config.md +++ b/start/config.md @@ -93,7 +93,7 @@ When set to **true**, error messages will be detailed with a stack trace and sni #### Logging -You may wish to log any errors that occur in your application. Laravel makes it a breeze. You can turn on logging by setting the log option to **true** in the **application/config/errors.php** file: +You may wish to log any errors that occur in your application. Laravel makes it a breeze. You can turn on logging by setting the log option to **true** in the **application/config/error.php** file: 'log' => true; From fc455c5f3249e300a3ea615a25cb10ace3cab202 Mon Sep 17 00:00:00 2001 From: Michael Grech Date: Sat, 5 Nov 2011 13:15:28 -0400 Subject: [PATCH 002/232] Fixed typeo. --- start/views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start/views.md b/start/views.md index 1a62e8d..9a5f748 100644 --- a/start/views.md +++ b/start/views.md @@ -193,7 +193,7 @@ Great! In this example, we are registering the **jquery-ui** asset, as well as s Asset::add('jquery-ui', 'js/jquery-ui.js', array('first', 'second')); -To increase response time, it is common to place JavaScript at the bottom of HTML documents. But, what if you also need to place some assets in the head of your document? No problem. The asset class provides a simple way to manage asset **containers**. Simple call the **container** method on the Asset class and mention the container name. Once you have a container instance, you are free to add any assets you wish to the container using the same syntax you are used to: +To increase response time, it is common to place JavaScript at the bottom of HTML documents. But, what if you also need to place some assets in the head of your document? No problem. The asset class provides a simple way to manage asset **containers**. Simply call the **container** method on the Asset class and mention the container name. Once you have a container instance, you are free to add any assets you wish to the container using the same syntax you are used to: Asset::container('footer')->add('example', 'js/example.js'); From c1dbb9cd54e3ac3395d9a63b90c13f26f16389da Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 23 Nov 2011 13:17:59 -0600 Subject: [PATCH 003/232] updated docs for 2.0 --- auth/config.md | 34 ++++++---- auth/usage.md | 16 ++--- cache/config.md | 15 ++++- contents.md | 15 ++++- database/query.md | 22 +++++++ database/redis.md | 52 ++++++++++++++++ database/usage.md | 4 ++ other/crypt.md | 8 +-- other/text.md | 4 +- session/config.md | 30 ++++++++- start/config.md | 10 ++- start/controllers.md | 136 ++++++++++++++++++++++++++++++++++++++++ start/interaction.md | 20 +++++- start/ioc.md | 51 +++++++++++++++ start/libraries.md | 43 +------------ start/modules.md | 67 -------------------- start/routes.md | 29 +++------ start/validation.md | 33 ++++++++-- start/views.md | 143 +++++++++++++++++++++++++++++++++---------- 19 files changed, 534 insertions(+), 198 deletions(-) create mode 100644 database/redis.md create mode 100644 start/controllers.md create mode 100644 start/ioc.md delete mode 100644 start/modules.md diff --git a/auth/config.md b/auth/config.md index a9be4d6..0c12bc0 100644 --- a/auth/config.md +++ b/auth/config.md @@ -2,29 +2,41 @@ Most interactive applications have the ability for users to login and logout. Obvious, right? Laravel provides a simple class to help you validate user credentials and retrieve information about the current user of your application. -The quickest way to get started is to create an [Eloquent User model](/docs/database/eloquent) in your **application/models** directory: +The quickest way to get started is to create an [Eloquent model](/docs/database/eloquent) in your **application/models** directory: class User extends Eloquent {} -Next, you will need to define **email** and **password** columns on your user database table. The password column should hold 60 alpha-numeric characters. The Auth class **requires** that all passwords be hashed and salted. - -> **Note:** The password column on your user table must really be named "password". +Next, let's define **email** and **password** columns on your user database table. The password column should hold 60 alpha-numeric characters. Great job! You're ready to start using the Auth class. However, there are more advanced configuration options available if you wish to use them. -Let's dig into the **application/config/auth.php** file. In this file you will find two closures: **by\_id** and **by\_username**: +Let's dig into the **application/config/auth.php** file. In this file you will find three closures: **user**, **attempt**, and **logout**: - 'by_id' => function($id) + 'user' => function($id) { - return User::find($id); + if ( ! is_null($id) and filter_var($id, FILTER_VALIDATE_INT) !== false) + { + return User::find($id); + } } -The **by_id** function is called when the Auth class needs to retrieve a user by their primary key. As you can see, the default implementation of this function uses an "User" Eloquent model to retrieve the user by ID. However, if you are not using Eloquent, you are free to modify this function to meet the needs of your application. +The **user** function is called when the Auth class needs to retrieve a user by their primary key. As you can see, the default implementation of this function uses an "User" Eloquent model to retrieve the user by ID. However, if you are not using Eloquent, you are free to modify this function to meet the needs of your application. - 'by_username' => function($username) + 'attempt' => function($username, $password, $config) { - return User::where('email', '=', $username)->first(); + $user = User::where($config['username'], '=', $username)->first(); + + if ( ! is_null($user) and Hash::check($password, $user->password)) + { + return $user; + } } -The **by_username** function is called when the Auth class needs to retrieve a user by their username, such as when using the **login** method. The default implementation of this function uses an "User" Eloquent model to retrieve the user by e-mail address. However, if you are not using Eloquent or do not wish to use e-mail addresses as usernames, you are free to modify this function as you wish as long as you return an object with **password** and **id** properties. \ No newline at end of file +The **attempt** function is called when the Auth class needs to retrieve a user by their username, such as when using the **Auth::attempt** method to login a user. The default implementation of this function uses an "User" Eloquent model to retrieve the user, then verifies that the given password matches the password stored in the database. The authentication configuration array is passed to the function, giving you convenient access to the **username** option you have specified. + +If the user is authenticated, you should return the user object. Just make sure the object you return has an "id" property. The ID will be stored in the session by the Auth class so the user can be identified on subsequent requests to your application. + + 'logout' => function($user) {} + +The **logout** function is called when the **Auth::logout** method is called. This function gives you a convenient location to interact with any third-party authentication providers you may be using. \ No newline at end of file diff --git a/auth/usage.md b/auth/usage.md index 65c35aa..5c8ea76 100644 --- a/auth/usage.md +++ b/auth/usage.md @@ -11,7 +11,7 @@ ### Salting & Hashing -If you are using the Auth class, Laravel requires all passwords to be hashed and salted. Web development must be done responsibly. Salted, hashed passwords make a rainbow table attack against your user's passwords impractical. +If you are using the Auth class, you are strongly encouraged to hash and salt all passwords. Web development must be done responsibly. Salted, hashed passwords make a rainbow table attack against your user's passwords impractical. Don't worry, salting and hashing passwords is easy using the **Hash** class. The Hash class provides a simple way to hash passwords using the **bcrypt** hashing algorithm. Check out this example: @@ -26,20 +26,20 @@ You can compare an unhashed value against a hashed one using the **check** metho return 'The password is valid!'; } -> **Note:** Before using the Auth class, be sure to [create the "password" column](/docs/auth/config) on your user table. - ### Logging In -Logging a user into your application is simple using the **login** method on the Auth class. Simply pass the username and password of the user to the method. The login method will return **true** if the credentials are valid. Otherwise, **false** will be returned: +Logging a user into your application is simple using the **attempt** method on the Auth class. Simply pass the username and password of the user to the method. The login method will return **true** if the credentials are valid. Otherwise, **false** will be returned: - if (Auth::login('example@gmail.com', 'password')) + if (Auth::attempt('example@gmail.com', 'password')) { return Redirect::to('user/profile'); } If the user's credentials are valid, the user ID will be stored in the session and the user will be considered "logged in" on subsequent requests to your application. +> **Note:** To provide more flexiblity when working with third-party authentication providers, you are not required to pass a password into the **attempt** method. + To determine if the user of your application is logged in, call the **check** method: if (Auth::check()) @@ -47,9 +47,11 @@ To determine if the user of your application is logged in, call the **check** me return "You're logged in!"; } -Sometimes you may need to login a user without checking their credentials, such as after a user first registers to use your application. It's easy using the **remember** method. Just pass your user object: +Sometimes you may need to login a user without checking their credentials, such as after a user first registers to use your application. It's easy using the **login** method. Just pass your user object or the user's ID: + + Auth::login($user); - Auth::remember($user); + Auth::login(15); ### Protecting Routes diff --git a/cache/config.md b/cache/config.md index 0045736..c064936 100644 --- a/cache/config.md +++ b/cache/config.md @@ -1,15 +1,17 @@ ## Cache Configuration - [Memcached](#memcached) +- [Redis](#redis) - [Cache Keys](#keys) Imagine your application displays the ten most popular songs as voted on by your users. Do you really need to look up these ten songs every time someone visits your site? What if you could store them for 10 minutes, or even an hour, allowing you to dramatically speed up your application? Caching makes it simple. -Laravel provides three wonderful cache drivers out of the box: +Laravel provides four wonderful cache drivers out of the box: - File System - Memcached - APC +- Redis By default, Laravel is configured to use the **file** system cache driver. It's ready to go. The file system driver stores cached items as files in the **application/storage/cache** directory. If you're satisfied with this driver, no other configuration is required. You're ready to start using it. @@ -30,9 +32,18 @@ Next, add your Memcached servers to the **servers** array: array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), ) + +### Redis + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [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), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + +Before using the Redis cache driver, you must [configure your Redis servers](/docs/database/redis#config). All done? Now you can just set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'redis' + ### Cache Keys -To avoid naming collisions with other applications using APC or a Memcached server, Laravel prepends a **key** to each item stored in the cache using these drivers. Feel free to change this value: +To avoid naming collisions with other applications using APC, Redis, or a Memcached server, Laravel prepends a **key** to each item stored in the cache using these drivers. Feel free to change this value: 'key' => 'laravel' \ No newline at end of file diff --git a/contents.md b/contents.md index c48ce4b..62eae78 100644 --- a/contents.md +++ b/contents.md @@ -9,6 +9,14 @@ - [Route Filters](/docs/start/routes#filters) - [Route Dependencies](/docs/start/routes#dependencies) - [Organizing Routes](/docs/start/routes#organize) +- [Controllers](/docs/start/controllers) + - [Creating Controllers](/docs/start/controllers#define) + - [Adding Actions](/docs/start/controllers#actions) + - [RESTful Actions](/docs/start/controllers#restful) + - [Action Filters](/docs/start/controllers#filters) + - [Layouts](/docs/start/controllers#layouts) + - [Dependency Injection](/docs/start/controllers#di) + - [Routes To Controllers](/docs/start/controllers#routes) - [Views & Responses](/docs/start/views) - [Creating Views](/docs/start/views#create) - [Binding Data To Views](/docs/start/views#bind) @@ -21,15 +29,17 @@ - [Building URLs](/docs/start/views#urls) - [Building HTML](/docs/start/views#html) - [Pagination](/docs/start/views#pagination) + - [Blade Templating](/docs/start/views#blade) + - [Sections](/docs/start/views#sections) - [Errors](/docs/start/views#errors) - [Interaction](/docs/start/interaction) - [Input](/docs/start/interaction#basics) - [Old Input](/docs/start/interaction#old) - [Cookies](/docs/start/interaction#cookies) - [Building Forms](/docs/start/interaction#forms) +- [Models & Libraries](/docs/start/libraries) - [Data Validation](/docs/start/validation) -- [Models, Libraries, & Packages](/docs/start/libraries) -- [Modules](/docs/start/modules) +- [IoC Container](/docs/start/ioc) ## Database @@ -37,6 +47,7 @@ - [Usage](/docs/database/usage) - [Fluent Query Builder](/docs/database/query) - [Eloquent ORM](/docs/database/eloquent) +- [Redis](/docs/database/redis) ## Caching diff --git a/database/query.md b/database/query.md index 70dda78..83b2a6e 100644 --- a/database/query.md +++ b/database/query.md @@ -7,6 +7,7 @@ - [Ordering Results](#ordering) - [Skip & Take](#limit) - [Aggregates](#aggregates) +- [Expressions](#expressions) - [Inserting Records](#insert) - [Updating Records](#update) - [Deleting Records](#delete) @@ -37,6 +38,10 @@ Instead of returning an array, the **first** method will return a single object: echo $user->email; +When you only need to retrieve the value of a single column, you may use the **only** method: + + $email = DB::table('users')->where('id', '=', 1)->only('email'); + It's easy to limit the columns returned by your query. Simply pass an array of columns you want into the **get** or **first** method: $user = DB::table('users')->get(array('id', 'email as user_email')); @@ -178,6 +183,23 @@ Of course, you may wish to limit the query using a WHERE clause first: $count = DB::table('users')->where('id', '>', 10)->count(); + +### Expressions + +Sometimes you may need to set the value of a column to a SQL function such as **NOW()**. It's a breeze using the **raw** method on the **DB** class. Here's what it looks like: + + DB::table('users')->update(array('updated_at' => DB::raw('NOW()'))); + +The **raw** method simply tells the query to inject the contents of the expression into the query as a string rather than a bound parameter. For example, you can also use expressions to increment column values: + + DB::table('users')->update(array('votes' => DB::raw('votes + 1'))); + +Of course, convenient methods are provided for **increment** and **decrement**: + + DB::table('users')->increment('votes'); + + DB::table('users')->decrement('votes'); + ### Inserting Records diff --git a/database/redis.md b/database/redis.md new file mode 100644 index 0000000..b97c3ec --- /dev/null +++ b/database/redis.md @@ -0,0 +1,52 @@ +## Redis + +- [Configuration](#config) +- [Usage](#usage) + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [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), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + + +### Configuration + +The Redis configuration for your application lives in the **application/config/database.php** file. Within this file, you will see a **redis** array containing the Redis servers used by your application: + + 'redis' => array( + + 'default' => array('host' => '127.0.0.1', 'port' => 6379), + + ), + +The default server configuration should suffice for development. However, you are free to modify this array based on your environment. Simply give each Redis server a name, and specify the host and port used by the server. + + +### Usage + +Laravel's Redis client is amazingly simple, yet beautiful. You may get a Redis instance by calling the **db** method on the **Redis** class: + + $redis = Redis::db(); + +This will give you an instance of the **default** Redis server. You may pass the server name to the **db** method to get a specific server as defined in your Redis configuration: + + $redis = Redis::db('redis_2'); + +Great! Now that we have an instance of the Redis client, we may issue any of the [Redis commands](http://redis.io/commands) to the instance. Laravel uses magic methods to pass the commands to the Redis server: + + $redis->set('name', 'Taylor'); + + $name = $redis->get('name'); + + $values = $redis->lrange('names', 5, 10); + +Notice the arguments to the comment are simply passed into the magic method. Isn't it a breeze? Of course, you are not required to use the magic methods, you may also pass commands to the server using the **run** method: + + $values = $redis->run('lrange', array(5, 10)); + +Just want to execute commands on the default Redis server? It gets even easier. You can just use static magic methods on the Redis class: + + Redis::set('name', 'Taylor'); + + $name = Redis::get('name'); + + $values = Redis::lrange('names', 5, 10); + +> **Note:** Redis [cache](/docs/cache/config#redis) and [session](/docs/session/config#redis) drivers are included with Laravel. \ No newline at end of file diff --git a/database/usage.md b/database/usage.md index ffa8d0c..7bc1934 100644 --- a/database/usage.md +++ b/database/usage.md @@ -20,6 +20,10 @@ When you need to get only one record from a table, use the **first** method. The $user = DB::first('select * from users where id = 1'); +Soemtimes you may need to only retrieve a single column from the database. You can use the **only** method to only retrieve that column's value from the query instead of an entire object: + + $email = DB::only('select email from users where id = 1'); + ### Connections Need to get the raw PDO object for a connection? It's easy. Just mention the connection name to the **connection** method on the DB class: diff --git a/other/crypt.md b/other/crypt.md index 45a52c9..88c63de 100644 --- a/other/crypt.md +++ b/other/crypt.md @@ -20,17 +20,17 @@ Wonderful. You're ready to start encrypting. ### Encrypting A String -Encrypting a string is a breeze. Just pass it to the **encrypt** method on a Crypter instance: +Encrypting a string is a breeze. Just pass it to the **encrypt** method on the Crypter class: - Crypter::make()->encrypt($value); + Crypter::encrypt($value); Do you feel like James Bond yet? ### Decrypting A String -So you're ready to decrypt a string? It's simple. Just use the **decrypt** method on a Crypter instance: +So you're ready to decrypt a string? It's simple. Just use the **decrypt** method on the Crypter class: - Crypter::make()->decrypt($encrypted_value); + Crypter::decrypt($encrypted_value); > **Note:** The decrypt method will only decrypt strings that were encrypted using **your** application key. \ No newline at end of file diff --git a/other/text.md b/other/text.md index 7cafafc..f05456d 100644 --- a/other/text.md +++ b/other/text.md @@ -39,6 +39,6 @@ To get the singular form of a word, use the **singular** method: echo Inflector::singular('friends'); -But, what if you are doing something like displaying the total number of comments on a blog post? You only want the plural form of "comment" if there is more than one. **Laravel** has you covered with the **plural_if** method. If the count passed to the method is greater than one, the plural form of the word will be returned. Otherwise, the word will be returned unchanged. +But, what if you are doing something like displaying the total number of comments on a blog post? You only want the plural form of "comment" if there is more than one. Just pass the number of comments into the **plural** method. If the count passed to the method is greater than one, the plural form of the word will be returned. Otherwise, the word will be returned unchanged. - echo Inflector::plural_if('comment', $count); \ No newline at end of file + echo Inflector::plural('comment', $count); \ No newline at end of file diff --git a/session/config.md b/session/config.md index 1904d40..11c28a2 100644 --- a/session/config.md +++ b/session/config.md @@ -1,19 +1,34 @@ ## Session Configuration +- [Cookie Sessions](#cookie) - [File System Sessions](#file) - [Database Sessions](#database) - [Memcached Sessions](#memcached) +- [Redis Sessions](#redis) The web is a stateless environment. This means that each request to your application is considered unrelated to any previous request. However, **sessions** allow you to store arbitrary data for each visitor to your application. The session data for each visitor is stored on your web server, while a cookie containing a **session ID** is stored on the visitor's machine. This cookie allows your application to "remember" the session for that user and retrieve their session data on subsequent requests to your application. Sound complicated? If so, don't worry about it. Just tell Laravel where to store the sessions and it will take care of the rest. -Three great session drivers are available out of the box: +> **Note:** Before using sessions, make sure an application key has been specified in the **application/config/application.php** file. +Five great session drivers are available out of the box: + +- Cookie - File System - Database - Memcached +- Redis + + +### Cookie Sessions + +Cookie based sessions provide a light-weight and fast mechanism for storing session information. They are also secure. Each cookie is encrypted using strong AES-256 encryption. However, cookies have a four kilobyte storage limit, so you may wish to use another driver if you are storing a lot of data in the session. + +To get started using cookie sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'cookie' ### File System Sessions @@ -63,8 +78,17 @@ Great! All you need to do now is set the driver in the **application/config/sess ### Memcached Sessions -Before using Memcached sessions, you must [configure your Memcached servers](/docs/cache/config#memcached). +Before using Memcached sessions, you must [configure your Memcached servers](/docs/database/config#memcached). All done? Great! Just set the driver in the **application/config/session.php** file: - 'driver' => 'memcached' \ No newline at end of file + 'driver' => 'memcached' + + +### Redis Sessions + +Before using Redis sessions, you must [configure your Redis servers](/docs/database/redis#config). + +Finished? Awesome! Just set the driver in the **application/config/session.php** file: + + 'driver' => 'redis' \ No newline at end of file diff --git a/start/config.md b/start/config.md index aef0144..8433f69 100644 --- a/start/config.md +++ b/start/config.md @@ -73,6 +73,7 @@ After setting up HTTP rewriting, you should set the **index** configuration opti - [404 Errors](#error-404) - [Error Detail](#error-detail) - [Logging](#error-logging) +- [Ignoring Errors](#ignoring-errors) #### 404 Errors @@ -86,7 +87,7 @@ You can easily control the level of error detail via the **detail** option in th 'detail' => true; -When set to **true**, error messages will be detailed with a stack trace and snippet of the relevant file. When set to **false**, the generic error page (**application/views/error/500.php**) will be displayed. Feel free to modify this view. +When set to **true**, error messages will be detailed with a stack trace and file information. When set to **false**, the generic error page (**application/views/error/500.php**) will be displayed. Feel free to modify this view. > **Note:** In a production environment, it is strongly suggested that you turn off error details. @@ -99,4 +100,9 @@ You may wish to log any errors that occur in your application. Laravel makes it You have total control over how your errors are logged via the **logger** function defined in **application/config/error.php**. This function is called every time there is an unhandled error or exception in your application. -As you can see, the default logger implementation writes to the **application/storage/log.txt** file; however, you are free to modify this function however you wish. \ No newline at end of file +As you can see, the default logger implementation writes to the **application/storage/log.txt** file; however, you are free to modify this function however you wish. + + +#### Ignoring Errors + +Sometimes you may wish to ignore certain errors, such as errors regarding deprecation or warnings. All you need to do is set the error levels you wish to ignore on the **ignore** option defined in **application/config/error.php**. These errors will not be displayed by Laravel; however, they will still be logged if error logging is enabled. \ No newline at end of file diff --git a/start/controllers.md b/start/controllers.md new file mode 100644 index 0000000..83c2bb9 --- /dev/null +++ b/start/controllers.md @@ -0,0 +1,136 @@ +## Controllers + +- [Creating Controllers](/docs/start/controllers#define) +- [Adding Actions](/docs/start/controllers#actions) +- [RESTful Actions](/docs/start/controllers#restful) +- [Action Filters](/docs/start/controllers#filters) +- [Layouts](/docs/start/controllers#layouts) +- [Dependency Injection](/docs/start/controllers#di) +- [Routes To Controllers](/docs/start/controllers#routes) + + +## Creating Controllers + +Maybe you want to take a more conventional MVC approach and use controllers? No problem. Using controllers in Laravel is amazingly simple. You will love it. + +A simple controller is included with Laravel. Just open up the **application/controllers/home.php** file. Notice that the controller has an **action\_index** method which returns the Laravel splash page. To make your application use this controller action instead of the default route, simply remove the default route from the **application/routes.php** file. Does the splash page still show up when you access your application? Great! You are now using controller's to drive your application. + +All controllers should live in the **application/controllers** directory. Controller class names use a **Foo\_Controller** convention, where "Foo" is the name of your controller. So, for example, a "user" controller would look something like this: + + class User_Controller extends Controller {} + +Notice that the controller extends the base **Controller** class. This class provides some core functionality used by every Laravel controller. Also, even though the class name is **User\_Controller**, the controller class should be stored in **application/controllers/user.php**. + +When creating nested controllers, the controller's name should reflect its location within the **controllers** directory. For example, a controller living at **application/controllers/user/profile.php** should have a class name of **User_Profile_Controller**. This controller will handle all requests beginning with **http://example.com/user/profile**. You are free to nest controllers as deep as you wish! + + +## Adding Actions + +You may be wondering how Laravel knows which controller and action to call. It's simple. The first segment of the request URI is considered the controller name, and the second segment of the request URI is considered the action name. Any remaining URI segments are passed to the controller action as parameters. + +For example, a request to **http://example.com/user/index** would call the **action\_index** method on the **User\_Controller**. But what about requests to **http://example.com**? These requests will use the default controller and action, which is **Home\_Controller** and **action\_index**. Of course, if the request only specifies the controller, the **action_index** will still be called. + +Adding your own actions to controllers couldn't be easier. You may have noticed that each action method is prefixed with **action\_**. This allows you to specify actions which have the same name as native PHP functions, such as **list**. So, let's add a "profile" action to our user controller: + + public function action_profile($id) {} + +Notice that this action accepts an **id** as an argument. Remember how any extra URI segments are passed to the action as parameters? That means a request to **http://example.com/user/profile/25** would call our **profile** method on the **User_Controller**, and would pass **25** into the action as a parameter. + + +## RESTful Actions + +You probably noticed that controller actions typically respond to all HTTP verbs. However, it's a breeze to setup controller actions to respond based on the request's HTTP verb. Just set the **restful** property on the controller: + + class User_Controller extends Controller { + + public $restful = true; + + } + +Great! Now, instead of prefixing your actions with **action**, you can prefix them with the HTTP verb they should respond to: + + public function get_index() {} + + public function post_index() {} + +It couldn't be simpler. RESTful controllers provide a beautiful compliment to typical controllers. Enjoy. + + +## Action Filters + +If you have used Laravel's RESTful routes, you probably love how easy it is to specify filters on the route. It's just as easy with controllers. Just use the **filter** method within your controller's constructor: + + public function __construct() + { + $this->filter('before', 'auth'); + } + +This will attach the **auth** filter to all of the controller's actions. However, sometimes you may need to attach a filter to a few actions. It's a breeze using the **only** and **except** methods: + + public function __construct() + { + $this->filter('before', 'csrf')->only('login'); + $this->filter('before', 'auth')->except('logout'); + } + +You may even attach filters to run only when a given HTTP verb is used for the request. Just use the **on** method: + + public function __construct() + { + $this->filter('before', 'csrf')->on('post'); + } + +Of course, you may specify as many filters and methods as you wish using arrays: + + public function __construct() + { + $this->filter('before', array('auth', 'csrf')) + ->only(array('login', 'profile')); + } + + +## Layouts + +Your application probably uses a common layout across all of its pages. Manually creating this layout within every controller action can be a pain. Specifying a controller layout will make your development much more enjoyable. To get started, simply add a **layout** property to your controller: + + class User_Controller extends Controller { + + public $layout = 'layouts.common'; + + } + +Now you may access the layout view from within your controller actions: + + public function action_profile() + { + $this->layout->content = View::make('user.profile'); + } + +It couldn't be simpler. Unlike other frameworks, there is no need for a separate, clunky template controller. Also, when using a layout, you do not need to explicitly return the layout at the end of your action. Laravel is smart enough to know you are using a layout, and will automatically consider the layout the response from the action. + + +## Dependency Injection + +> **Note:** Before diving into controller dependency injection, you may wish to read the documentation on Laravel's beautiful [IoC container](/docs/ioc/config). + +If you are focusing on writing testable code, you will probably want to inject dependencies into the constructor of your controller. No problem. Just register your controller in the [IoC container](/docs/start/ioc). When registering the controller with the container, prefix the key with **controllers.**. So, we could register our user controller like so: + + 'controllers.user' => array('resolver' => function() + { + return new User_Controller; + }) + +When a request to a controller enters your application, Laravel will automatically determine if the controller is registered in the container, and if it is, will use the container to resolve an instance of the controller. + + +## Routes To Controllers + +Sometimes you may wish to point a RESTful route to a controller. For example, perhaps you want to point several URIs to the same controller action. It couldn't be simpler. In your **application/routes.php** file, simply specify which controller and action should be used by the route: + + 'GET /user/(:num)/edit' => 'user@edit' + +Notice the **user@edit** pointer. This tells Laravel to call the **action_edit** method on the **User_Controller**. Of course, we can call a nested controller using "dot" notation. + + 'GET /user/(:num)/edit' => 'user.profile@edit' + +This would call the **action_edit** method on the **User_Profile_Controller**. The route wildcards will automatically be passed to the controller action as parameters. Isn't it simple? \ No newline at end of file diff --git a/start/interaction.md b/start/interaction.md index 1431282..8be1a74 100644 --- a/start/interaction.md +++ b/start/interaction.md @@ -54,6 +54,22 @@ Have you ever tried to re-populate an input form after an invalid form submissio $name = Input::old('name'); +Behind the scenes, this method is retrieving the previous request's input from the session. So, how does the input get in the session? It's simple. You can use the **flash** method on the **Input** class: + + Input::flash(); + +Of course, flashing passwords or other sensitive information to the session is a bad idea. So, you can filter the data flashed to the session like this: + + Input::flash('only', array('username', 'email')); + + Input::flash('except', array('password', 'credit_card')); + +Since you will typically want to flash input right before a redirect, there is a beautiful shortcut to **Input::flash** on the **Redirect** class. Just call **with\_input** + + return Redirect::to('login')->with_input(); + + return Redirect::to('login')->with_input('except', array('password')); + > **Note:** You must specifiy a session driver before using the **old** Input method. As you would expect, you may pass a default value in the second parameter to the method: @@ -146,9 +162,9 @@ Laravel provides an easy method of protecting your application from [cross-site Now, simply [attach the built-in CSRF filter](/docs/start/routes#filters) to the route the form is posting to. If the token submitted by the form does not match the token in the user's session, the **application/views/error/500.php** view will be displayed. -Want to just get the CSRF token without generating a hidden input field? Use the **raw_token** method: +Want to just get the CSRF token without generating a hidden input field? Use the **token** method on the **Session** class: - echo Form::raw_token(); + echo Session::token(); > **Note:** Don't forget to [specify a session driver](/docs/session/config) before using these methods. diff --git a/start/ioc.md b/start/ioc.md new file mode 100644 index 0000000..47868a4 --- /dev/null +++ b/start/ioc.md @@ -0,0 +1,51 @@ +## IoC Container + +- [Definition](/docs/start/ioc#definition) +- [Registering Objects](/docs/start/ioc#register) +- [Resolving Objects](/docs/start/ioc#resolve) + + +## Definition + +An IoC container is simply a way of managing the creation of objects. You can use it to define the creation of complex objects, allowing you to resolve them throughout your application using a single line of code. You may also use it to "inject" dependencies into your classes and controllers. + +IoC containers help make your application more flexible and testable. Since you may register alternate implementations of an interface with the container, you may isolate the code you are testing from external dependencies using [stubs and mocks](http://martinfowler.com/articles/mocksArentStubs.html). + + +## Registering Objects + +The resolvers for all items in the IoC container are defined in the **application/config/container.php** file. By default, there is nothing registered in the container. But, perhaps your application is using [SwiftMailer](http://swiftmailer.org/). The creation of a SwiftMailer instance can be a little cumbersome, so let's register it in the container: + + 'mailer' => array('resolver' => function() + { + require_once LIBRARY_PATH.'SwiftMailer/lib/swift_required'; + + $transport = Swift_MailTransport::newInstance(); + + return Swift_Mailer::newInstance($transport); + }) + +Great! Now we have registered a resolver for SwiftMailer in our container. But, what if we don't want the container to create a new mailer instance every time we need one? Maybe we just want the container to return the same instance after the intial instance is created. It's easy. Just tell the container the object should be a singleton: + + 'mailer' => array('singleton' => true, 'resolver' => function() + { + ... + }) + + +## Resolving Objects + +Now that we have SwiftMailer registered in the container, we can resolve it using the **resolve** method on the **IoC** class: + + $mailer = IoC::resolve('mailer'); + +Of course, we could leverage the IoC container to accomplish dependency injection. Let's inject the mailer instance into another class: + + 'repository.user' => array('resolver' => function() + { + return new User_Repository(IoC::resolve('mailer')); + }) + +Note that we called the **resolve** method from within a resolver! This allows us to easily resolve deeply nested dependencies. This makes our repository more testable, since we can now inject a stub of SwiftMailer when testing the repository, allowing us to isolate the behavior of the repository from SwiftMailer. + +> **Note:** You may also [register controllers in the container](/docs/start/controllers#di). \ No newline at end of file diff --git a/start/libraries.md b/start/libraries.md index c8236fc..c0225fd 100644 --- a/start/libraries.md +++ b/start/libraries.md @@ -1,8 +1,7 @@ -## Models, Libraries, & Packages +## Models & Libraries - [Models](#models) - [Libraries](#libraries) -- [Packages](#packages) ## Models @@ -14,42 +13,4 @@ Models are the meat of your application, and they live in the **application/mode Libraries can be considered the "helper" classes of your application. Usually, they are classes that do not contain logic specific to your application; however, they are important to its functionality. For example, a Twitter library may be used to fetch your recent tweets and display them on a view. Fetching Tweets from Twitter isn't specific to your application, but it is still important. Place your generic, helper libraries in the **application/libraries** directory. Like models, libraries are also auto-loaded by Laravel. - -## Packages - -- [The Basics](#package-basics) -- [Auto-Loading Packages](#package-auto) -- [Registering Load Paths](#package-register) - - -### The Basics - -Packages and libraries have many things in common. Often, they provide functionality that is not specific to your application. However, packages are generally not written specifically for Laravel. For example, the wonderful e-mailing package [SwiftMailer](http//swiftmailer.org) is not written just for Laravel, but it can be used in a variety of frameworks, including Laravel. All packages live in the **packages** directory. - -Often, it is necessary for packages to be bootstrapped. For example, the package may need to register its own auto-loader. Not a problem. The **package** class provides an easy way to load packages. If a **bootstrap.php** file is present in the package root directory, it will be run. - - Package::load('swift-mailer'); - -Need to load more than one package? Just pass an array to the **load** method: - - Package::load(array('swift-mailer', 'facebook')); - -> **Note:** Package classes are not auto-loaded by Laravel. Each package is responsible for registering its own auto-loader. - - -### Auto-Loading Packages - -Sometimes you may want to load a package for every request to your application. No problem. Just add the package to the **packages** option in the **application/config/application.php** file: - - 'packages' => array('swift-mailer') - -Alternatively, you can use the [**needs** keyword on a route](/docs/start/routes#dependencies). - - -### Registering Load Paths - -Have you written a package and want its classes to be auto-loaded by Laravel? Checkout the **register** method on the **Loader** class. All it needs is a directory: - - Loader::register('path/to/package/root'); - -Once a directory has been registered, the Laravel auto-loader will search the directory for classes in exactly the same way it searches the application **models** and **libraries** directories. \ No newline at end of file +The PSR-0 naming standard is becoming more popular among PHP developers. Because of this, full support for PSR-0 auto-loading is provided by Laravel. To use a PSR-0 library, simply drop it in the **application/libraries** directory and Laravel will take care of the rest! \ No newline at end of file diff --git a/start/modules.md b/start/modules.md deleted file mode 100644 index 5ac5d41..0000000 --- a/start/modules.md +++ /dev/null @@ -1,67 +0,0 @@ -## Modules - -- [The Basics](#basics) -- [Setting Up A Module](#setup) -- [Module Views](#views) -- [Module Models & Libraries](#libraries) -- [Module Configuration & Language](#config) - - -### The Basics - -Modules are an amazing way to organize your application. All modules live in the **modules** directory. Picture each module as a sort of sub-application living within your main Laravel application. A module can have its own libraries, models, routes, views, composers, filters, language files, and configuration. Ready to learn how to use them? Let's jump in. - - -### Setting Up A Module - -To setup a module, create a directory in the **modules** directory. For example, you might create an **admin** directory under the **modules** directory. Great! The **admin** module will respond to all requests to your application beginning with **/admin**. Sound familiar? That's because it is similar to [using a route folder](/docs/start/routes#organize). - -So, within your **modules/admin** directory, create a **routes.php** file with the following route: - - 'GET /admin' => function() - { - return 'Hello Admins!'; - } - -Next, in your **application/config/application.php** file, add **admin** to the array of active modules: - - 'modules' => array('admin') - -Now, browse to this URL in your web browser. See "Hello Admins!"? Wonderful. You're learning fast. - -> **Note:** Module route filters work just like application route filters. Just create a **filters.php** file within your module directory. - - -### Module Views - -To create a view that will be used by your module, simply create a **views** directory within your module directory. Once you have created a view, you can get an instance of it like this: - - $view = View::make('module::view.name'); - -Notice the module qualifier followed by two colons? This tells Laravel which module the view belongs to. Of course, if the view is in the **application/views** directory, you do need to specify the module name. So, for example, if this view belongs to your **admin** module, you could create it like so: - - $view = View::make('admin::view.name'); - -> **Note:** Module composers work just like application composers. Just make a **composers.php** file within your module directory. - - -### Module Models & Libraries - -Using models and libraries within modules is very similar to using them within your application. However, module models and libraries must be namespaced using the module name. So, if you have a **User** model within your **admin** model, it should be namespaced like so: - - namespace Admin; - - class User {} - -Once you have created the model, you may use it anywhere within your application: - - $user = new Admin\User; - - -### Module Configuration and Language - -You can create module configuration and language files just like you create their application counterparts. To load them, use double-colon module qualifier, just like you load module views: - - Config::get('module::option.name'); - - Lang::line('module::language.line')->get(); \ No newline at end of file diff --git a/start/routes.md b/start/routes.md index 7b6f9af..322c43d 100644 --- a/start/routes.md +++ b/start/routes.md @@ -4,7 +4,6 @@ - [Wildcard URI Segments](/docs/start/routes#segments) - [Named Routes](/docs/start/routes#named) - [Route Filters](/docs/start/routes#filters) -- [Route Dependencies](/docs/start/routes#dependencies) - [Organizing Routes](/docs/start/routes#organize) Unlike other PHP frameworks, Laravel places routes and their corresponding functions in one file: **application/routes.php**. This file contains the "definition", or public API, of your application. To add functionality to your application, you add to the array located in this file. It's a breeze. @@ -90,9 +89,9 @@ Alright, ready to attach the filter to a route? Do it like this: Notice the route now has an array value with a **before** key. The **before** value contains the names of any filters that should be run before the method is executed. -Why stop with one filter? You can define multiple filters for a single route by separating the filter names with commas: +Why stop with one filter? You can define multiple filters for a single route by separating the filter names with pipes: - 'POST /user' => array('before' => 'auth, csrf', function() {}) + 'POST /user' => array('before' => 'auth|csrf', function() {}) Remember, if a "before" filter returns a value, that value will be considered the output of the request. For example, the built-in **auth** filter checks if the user has logged in to your application. If they haven't, a [Redirect](/docs/start/views#redirect) to the login page is sent to the browser. Isn't the simplicity refreshing? @@ -104,29 +103,19 @@ Of course, adding filters to run after the request is just as easy: > **Note:** "After" filters receive the response returned by the route function that handled the request. - -## Route Dependencies +### Filter Parameters -Often, a route needs to use a package to do its job. For instance, perhaps it needs to use the [SwiftMailer](http://swiftmailer.org) package to send some e-mails. One option is to load the package within your route like this: +To keep your code clean, you may wish to pass parameters to filters. For instance, you could create a **role** filter which accepted a role name. It's simple: - 'GET /' => function() - { - Package::load('swift-mailer'); - } + 'role' => function($role) {} -However, cluttering up your routes with package loading can get frusterating. Instead, tell Laravel what your route **needs**: +Now, you can attach the filter to a route like this: - 'GET /' => array('needs' => 'swift-mailer', function() - { - // - }) + 'GET /admin' => array('before' => 'role:admin', function() {}) -Now, the **swift-mailer** package will be loaded each time this route is called. Need to load more than one package? No problem: +Notice that a colon is used to separate the filter name from the filter parameters. Of course, you are welcome to pass more than one parameter to the filter by separating them with commas: - 'GET /' => array('needs' => 'swift-mailer, facebook', function() - { - // - }) + 'GET /admin' => array('before' => 'role:admin,editor', function() {}) ## Organizing Routes diff --git a/start/validation.md b/start/validation.md index 7949cf9..de8b99e 100644 --- a/start/validation.md +++ b/start/validation.md @@ -3,6 +3,7 @@ - [The Basics](#basics) - [Validation Rules](#rules) - [Retrieving Error Messages](#errors) +- [Error Messages & Views](#views) - [Specifying Custom Error Messages](#messages) - [Creating Custom Validation Rules](#custom) @@ -271,6 +272,30 @@ The **all** method returns an array containing all error messages for all attrib return $validator->errors->all('

:message

'); + +### Errors Messages & Views + +Once you have performed your validation, you need an easy way to get the errors back to the view. Laravel makes it amazingly simple. Let's walk through a typical scenario. We'll define two routes: + + 'GET /register' => function() + { + return View::make('user.register'); + }, + + 'POST /register' => function() + { + $validator = Validator::make(Input::all(), $rules); + + if ($validator->invalid()) + { + return Redirect::to('register')->with_errors($validator); + } + } + +Great! So, we have two simple registration routes. One to handle displaying the form, and one to handle the posting of the form. In the **POST** route, we run some validation over the input. If the validation fails, we redirect back to the registration form and [flash the validation errors to the session](/docs/start/views#with) so they will be available for us to display. + +But, notice we are not explicitly binding the errors to the view in our **GET** route. However, an **errors** variable will still be available in the view. Laravel intelligently determines if errors exist in the session, and if they do, binds them to the view for you. If no errors exist in the session, an empty message container will still be bound to the view. In your views, this allows you to always assume you have a message container available via the **errors** variable. We love making your life easier. + ### Specifying Custom Error Messages @@ -305,11 +330,11 @@ In the example above, the custom required message will be used for the **email** ### Creating Custom Validation Rules -Need to create your own validation rules? You will love how easy it is! First, create a class that extends **System\Validator** and place it in your **application/libraries** directory: +Need to create your own validation rules? You will love how easy it is! First, create a class that extends **Laravel\Validator** and place it in your **application/libraries** directory: @@ -35,7 +37,7 @@ When building a large application, you may want to organize your views into sub- 'GET /home' => function() { - return View::make('user/login'); + return View::make('user.login'); } It's that simple. Of course, you are not required to return a View. Strings are also welcome: @@ -48,11 +50,11 @@ It's that simple. Of course, you are not required to return a View. Strings are ## Binding Data To Views -You can pass data to a view by "binding" the data to a variable. This is done using the **bind** method on the View: +You can pass data to a view by "binding" the data to a variable. This is done using the **with** method on the View: 'GET /home' => function() { - return View::make('simple')->bind('email', 'example@gmail.com'); + return View::make('simple')->with('email', 'example@gmail.com'); } In the example above, the first parameter is the **name** of the view variable. The second parameter is the **value** that will be assigned to the variable. @@ -68,22 +70,24 @@ Of course, we can bind as many variables as we wish: 'GET /home' => function() { return View::make('simple') - ->bind('name', 'Taylor') - ->bind('email', 'example@gmail.com'); + ->with('name', 'Taylor') + ->with('email', 'example@gmail.com'); } You may also bind view data by simply setting properties on a view instance: 'GET /home' => function() { - $view = View::make('user/login'); + $view = View::make('user.login'); - $view->name = 'Taylor'; + $view->name = 'Taylor'; $view->email = 'example@gmail.com'; return $view; } +> **Note:** An **errors** variable is always bound to every view. Check out the [Validator documentation](/docs/start/validation#views) to learn why. + ## Nesting Views Within Views @@ -91,30 +95,30 @@ Want to nest views inside of views? There are two ways to do it, and they are bo 'GET /home' => function() { - $view = View::make('user/login'); + $view = View::make('user.login'); - $view->content = View::make('partials/content'); - $view->footer = View::make('partials/footer'); + $view->content = View::make('partials.content'); + $view->footer = View::make('partials.footer'); return $view; } -Nested calls to **View::make** can get a little ugly. For that reason, Laravel provides a simple **partial** method: +Nested calls to **View::make** can get a little ugly. For that reason, Laravel provides a simple **nest** method: - View::make('layout/default')->partial('content', 'partials/home'); + View::make('layout.default')->nest('content', 'partials.home'); -The **partial** method is very similar to the **bind** method; however, you simply pass a view name in the second parameter to the method. The view will created and bound to the variable for you. +The **nest** method is very similar to the **with** method; however, you simply pass a view name in the second parameter to the method. The view will created and bound to the variable for you. Need to bind data to a partial? No problem. Pass the data in the third parameter to the method: - View::make('layout/default') - ->partial('content', 'partials/home', array('name' => 'Taylor')); + View::make('layout.default') + ->nest('content', 'partials.home', array('name' => 'Taylor')); -In some situations, you may need to get the string content of a view from within another view. It's easy using the **get** method: +In some situations, you may need to get the string content of a view from within another view. It's easy using the **render** method: - get(); ?> - get(); ?> + render(); ?> + render(); ?> @@ -137,16 +141,16 @@ Of course, you may pass bindings into the **of** method: Since the **of** method returns an instance of the **View** class, you may use any of the View class methods: - return View::of_home()->bind('email', $email); + return View::of_home()->with('email', $email); Using named views makes templating a breeze: - return View::of_layout()->bind('content', $content); + return View::of_layout()->with('content', $content); ## View Composers -View composers will free you from repetitive, brittle code, and help keep your application beautiful and maintainable. All view composers are defined in the **application/composers.php** file. Each time a view is created, its composer will be called. The composer can bind data to the view, register its assets, or even gather common data needed for the view. When the composer is finished working with the view, it should return the view instance. Here's an example composer: +View composers will free you from repetitive, brittle code, and help keep your application beautiful and maintainable. All view composers are defined in the **application/composers.php** file. Each time a view is created, its composer will be called. The composer can bind data to the view, register its assets, or even gather common data needed for the view. Here's an example composer: return array( @@ -155,10 +159,8 @@ View composers will free you from repetitive, brittle code, and help keep your a Asset::add('jquery', 'js/jquery.js'); Asset::add('jquery-ui', 'js/jquery-ui.js', 'jquery'); - $view->partial('header', 'partials/header'); - $view->partial('footer', 'partials/footer'); - - return $view; + $view->nest('header', 'partials.header'); + $view->nest('footer', 'partials.footer'); } ); @@ -258,23 +260,39 @@ After a user creates an account or signs into your application, it is common to return Redirect::to('user/profile')->with('status', 'Welcome Back!'); +Now, let's say a user submitted a form and your validator found some problems. You probably want to redirect back to that form and repopulate it with the user's input. That means you need to flash the Input data to the session. Sound complicated? It's actually amazingly simple to do this using the **with_input** method on the **Redirect** class: + + return Redirect::to('register')->with_input(); + +Of course, flashing passwords or other sensitive information to the session is a bad idea. So, you can filter the data flashed to the session like this: + + return Redirect::to('register')->with_input('only', array('username')); + + return Redirect::to('register')->with_input('except', array('password')); + +Another common scenario is flashing validation errors to the session before redirect to a form. It's so easy. Just use the **with_errors** method on the **Redirect** class and pass in your [Validator](/docs/start/validation). The Validator's errors will automatically be flashed: + + return Redirect::to('register')->with_errors($validator); + +To learn more about working with validation errors and views, check out the [Validator documentation](/docs/start/validation#views). + > **Note:** For more information regarding Sessions and flash data, check out the [Session documentation](/docs/session/config). ## Downloads -Perhaps you just want to force the web browser to download a file? Check out the **download** method on the **File** class: +Perhaps you just want to force the web browser to download a file? Check out the **download** method on the **Response** class: 'GET /file' => function() { - return File::download('path/to/your/file.jpg'); + return Response::download('path/to/your/file.jpg'); } In the example above, the image will be downloaded as "file.jpg", however, you can easily specify a different filename for the download in the second parameter to the method: 'GET /file' => function() { - return File::download('path/to/your/file.jpg', 'photo.jpg'); + return Response::download('path/to/your/file.jpg', 'photo.jpg'); } @@ -535,7 +553,7 @@ The URLs created by the Pagination class look something like this: Sometimes, you may wish to add more items to the query string, such as the column you are sorting by. It's a breeze. Use the **append** method on your Paginator instance: - append(array('sort' => 'votes'))->links(); + appends(array('sort' => 'votes'))->links(); Great! Now the URLs will look like this: @@ -544,7 +562,7 @@ Great! Now the URLs will look like this: Need to style your links? No problem. All pagination link elements can be style using CSS classes. Here is an example of the HTML elements generated by the **links** method: