Commit 77f777bf authored by Taylor Otwell's avatar Taylor Otwell

Merge branch 'develop'

parents f9185ead 48706cae
...@@ -30,14 +30,12 @@ return array( ...@@ -30,14 +30,12 @@ return array(
'Inflector' => 'System\\Inflector', 'Inflector' => 'System\\Inflector',
'Input' => 'System\\Input', 'Input' => 'System\\Input',
'Lang' => 'System\\Lang', 'Lang' => 'System\\Lang',
'Log' => 'System\\Log',
'URL' => 'System\\URL', 'URL' => 'System\\URL',
'Redirect' => 'System\\Redirect', 'Redirect' => 'System\\Redirect',
'Request' => 'System\\Request', 'Request' => 'System\\Request',
'Response' => 'System\\Response', 'Response' => 'System\\Response',
'Session' => 'System\\Session', 'Session' => 'System\\Session',
'Str' => 'System\\Str', 'Str' => 'System\\Str',
'Text' => 'System\\Text',
'Validator' => 'System\\Validator', 'Validator' => 'System\\Validator',
'View' => 'System\\View', 'View' => 'System\\View',
......
...@@ -4,29 +4,39 @@ return array( ...@@ -4,29 +4,39 @@ return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Authentication Model | Retrieve Users By ID
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This model will be used by the Auth class when retrieving the users of | This method is called by the Auth::user() method when attempting to
| your application. Feel free to change it to the name of your user model. | retrieve a user by their user ID.
| |
| Note: The authentication model must be an Eloquent model. | You are free to change this method for your application however you wish.
| |
*/ */
'model' => 'User', 'by_id' => function($id)
{
return User::find($id);
},
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Authentication Username | Retrieve Users By Username
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The authentication username is the column on your users table that | This method is called by the Auth::check() method when attempting to
| is considered the username of the user. Typically, this is either "email" | retrieve a user by their username.
| or "username". However, you are free to make it whatever you wish. |
| You are free to change this method for your application however you wish.
|
| Note: This method must return an object that has an "id" and a "password"
| property. The type of object returned doesn't matter.
| |
*/ */
'username' => 'email', 'by_username' => function($username)
{
return User::where('email', '=', $username)->first();
},
); );
\ No newline at end of file
...@@ -24,11 +24,32 @@ return array( ...@@ -24,11 +24,32 @@ return array(
| Would you like errors to be logged? Error logging can be extremely | Would you like errors to be logged? Error logging can be extremely
| helpful when debugging a production application. | helpful when debugging a production application.
| |
| Note: When error logging is enabled, errors will be logged even when
| error detail is disabled.
|
*/ */
'log' => false, 'log' => false,
/*
|--------------------------------------------------------------------------
| Error Logger
|--------------------------------------------------------------------------
|
| Because of the sundry ways of managing error logging, you get complete
| flexibility to manage error logging as you see fit.
|
| This function will be called when an error occurs in your application.
| You can log the error however you like.
|
| The error "severity" passed to the method is a human-readable severity
| level such as "Parsing Error", "Fatal Error", etc.
|
| A simple logging system has been setup for you. By default, all errors
| will be logged to the application/log.txt file.
|
*/
'logger' => function($severity, $message)
{
System\File::append(APP_PATH.'storage/log.txt', date('Y-m-d H:i:s').' '.$severity.' - '.$message.PHP_EOL);
},
); );
\ No newline at end of file
...@@ -2,50 +2,25 @@ ...@@ -2,50 +2,25 @@
return array( return array(
/* "accepted" => "The :attribute must be accepted.",
|-------------------------------------------------------------------------- "active_url" => "The :attribute does not exist.",
| General Validation Messages "alpha" => "The :attribute may only contain letters.",
|-------------------------------------------------------------------------- "alpha_dash" => "The :attribute may only contain letters, numbers, dashes, and underscores.",
*/ "alpha_num" => "The :attribute may only contain letters and numbers.",
"between" => "The :attribute must be between :min - :max.",
"acceptance_of" => "The :attribute must be accepted.", "confirmed" => "The :attribute confirmation does not match.",
"confirmation_of" => "The :attribute confirmation does not match.", "email" => "The :attribute format is invalid.",
"exclusion_of" => "The :attribute value is invalid.", "image" => "The :attribute must be an image.",
"format_of" => "The :attribute format is invalid.", "in" => "The selected :attribute is invalid.",
"inclusion_of" => "The :attribute value is invalid.", "integer" => "The :attribute must be an integer.",
"presence_of" => "The :attribute can't be empty.", "max" => "The :attribute must be less than :max.",
"uniqueness_of" => "The :attribute has already been taken.", "mimes" => "The :attribute must be a file of type: :values.",
"with_callback" => "The :attribute is invalid.", "min" => "The :attribute must be at least :min.",
"not_in" => "The selected :attribute is invalid.",
/* "numeric" => "The :attribute must be a number.",
|-------------------------------------------------------------------------- "required" => "The :attribute field is required.",
| Numericality_Of Validation Messages "size" => "The :attribute must be :size.",
|-------------------------------------------------------------------------- "unique" => "The :attribute has already been taken.",
*/ "url" => "The :attribute format is invalid.",
"number_not_valid" => "The :attribute must be a number.",
"number_not_integer" => "The :attribute must be an integer.",
"number_wrong_size" => "The :attribute must be :size.",
"number_too_big" => "The :attribute must be no more than :max.",
"number_too_small" => "The :attribute must be at least :min.",
/*
|--------------------------------------------------------------------------
| Length_Of Validation Messages
|--------------------------------------------------------------------------
*/
"string_wrong_size" => "The :attribute must be :size characters.",
"string_too_big" => "The :attribute must be no more than :max characters.",
"string_too_small" => "The :attribute must be at least :min characters.",
/*
|--------------------------------------------------------------------------
| Upload_Of Validation Messages
|--------------------------------------------------------------------------
*/
"file_wrong_type" => "The :attribute must be a file of type: :types.",
"file_too_big" => "The :attribute exceeds size limit of :maxkb.",
); );
\ No newline at end of file
...@@ -7,13 +7,10 @@ return array( ...@@ -7,13 +7,10 @@ return array(
| Application Routes | Application Routes
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here is the "definition", or the public API, of your application. | Here is the public API of your application. To add functionality to your
| application, you just add to the array located in this file.
| |
| To add functionality to your application, you add to the array located | It's a breeze. Simply tell Laravel the request URIs it should respond to.
| in this file. It's a breeze. Just tell Laravel the request method and
| URI a function should respond to.
|
| To learn more, check out: http://laravel.com/docs/basics/routes
| |
*/ */
......
...@@ -4,53 +4,84 @@ ...@@ -4,53 +4,84 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>404 - Not Found</title> <title>404 - Not Found</title>
<link href='http://fonts.googleapis.com/css?family=Ubuntu&amp;subset=latin' rel='stylesheet' type='text/css'> <link href="http://fonts.googleapis.com/css?family=Quattrocento&amp;v1" rel="stylesheet" type="text/css" media="all" />
<link href="http://fonts.googleapis.com/css?family=Ubuntu&amp;v1" rel="stylesheet" type="text/css" media="all" />
<link href="http://fonts.googleapis.com/css?family=Lobster+Two&amp;v1" rel="stylesheet" type="text/css" media="all" />
<style type="text/css"> <style type="text/css">
body { body {
background-color: #fff; background-color: #eee;
margin: 45px 0 0 0; color: #6d6d6d;
font-family: 'Ubuntu', sans-serif; font-family: 'Ubuntu';
font-size: 16px; font-size: 16px;
color: #3f3f3f;
} }
h1 { a {
font-size: 40px; color: #7089b3;
margin: 0 0 10px 0; font-weight: bold;
text-decoration: none;
} }
a { h1.laravel {
color: #000; font-family: 'Lobster Two', Helvetica, serif;
font-size: 60px;
margin: 0 0 15px -10px;
padding: 0;
text-shadow: -1px 1px 1px #fff;
} }
#wrapper { h2 {
width: 740px; font-family: 'Quattrocento', serif;
font-size: 30px;
margin: 30px 0 0 0;
padding: 0;
text-shadow: -1px 1px 1px #fff;
}
p {
margin: 10px 0 0 0;
line-height: 25px;
}
#header {
margin: 0 auto; margin: 0 auto;
margin-bottom: 15px;
margin-top: 20px;
width: 80%;
} }
#content { #wrapper {
padding: 10px 10px 10px 10px; background-color: #fff;
background-color: #ffebe8;
border: 1px solid #dd3c10;
border-radius: 10px; border-radius: 10px;
margin: 0 auto;
padding: 10px;
width: 80%;
}
#wrapper h2:first-of-type {
margin-top: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="wrapper"> <div id="header">
<?php <?php
$messages = array('Should we ask for directions?', 'This doesn\'t look familiar.', 'We need a map.'); $messages = array("We're lost.", "This doesn't look familiar.", "We need a map.");
$message = $messages[mt_rand(0, 2)]; $message = $messages[mt_rand(0, 2)];
?> ?>
<h1><?php echo $message; ?></h1> <h1 class="laravel"><?php echo $message; ?></h1>
<div id="content">
The resource you requested was not found.
<br /><br />
Would you like go to our <a href="<?php echo System\Config::get('application.url'); ?>">home page</a> instead?
</div> </div>
<div id="wrapper">
<?php
$apologies = array("This is embarrassing.", "Don't give up on us.", "We're really sorry.");
$apology = $apologies[mt_rand(0, 2)];
?>
<h2><?php echo $apology; ?></h2>
<p>We couldn't find the resource you requested. Would you like go to our <a href="<?php echo System\Config::get('application.url'); ?>">home page</a> instead?</p>
</div> </div>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -4,53 +4,84 @@ ...@@ -4,53 +4,84 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>500 - Internal Server Error</title> <title>500 - Internal Server Error</title>
<link href='http://fonts.googleapis.com/css?family=Ubuntu&amp;subset=latin' rel='stylesheet' type='text/css'> <link href="http://fonts.googleapis.com/css?family=Quattrocento&amp;v1" rel="stylesheet" type="text/css" media="all" />
<link href="http://fonts.googleapis.com/css?family=Ubuntu&amp;v1" rel="stylesheet" type="text/css" media="all" />
<link href="http://fonts.googleapis.com/css?family=Lobster+Two&amp;v1" rel="stylesheet" type="text/css" media="all" />
<style type="text/css"> <style type="text/css">
body { body {
background-color: #fff; background-color: #eee;
margin: 45px 0 0 0; color: #6d6d6d;
font-family: 'Ubuntu', sans-serif; font-family: 'Ubuntu';
font-size: 16px; font-size: 16px;
color: #3f3f3f;
} }
h1 { a {
font-size: 40px; color: #7089b3;
margin: 0 0 10px 0; font-weight: bold;
text-decoration: none;
} }
a { h1.laravel {
color: #000; font-family: 'Lobster Two', Helvetica, serif;
font-size: 60px;
margin: 0 0 15px -10px;
padding: 0;
text-shadow: -1px 1px 1px #fff;
} }
#wrapper { h2 {
width: 740px; font-family: 'Quattrocento', serif;
font-size: 30px;
margin: 30px 0 0 0;
padding: 0;
text-shadow: -1px 1px 1px #fff;
}
p {
margin: 10px 0 0 0;
line-height: 25px;
}
#header {
margin: 0 auto; margin: 0 auto;
margin-bottom: 15px;
margin-top: 20px;
width: 80%;
} }
#content { #wrapper {
padding: 10px 10px 10px 10px; background-color: #fff;
background-color: #ffebe8;
border: 1px solid #dd3c10;
border-radius: 10px; border-radius: 10px;
margin: 0 auto;
padding: 10px;
width: 80%;
}
#wrapper h2:first-of-type {
margin-top: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="wrapper"> <div id="header">
<?php <?php
$messages = array('Whoops!', 'Oh no!', 'Ouch!'); $messages = array('Whoops!', 'Oh no!', 'Ouch!');
$message = $messages[mt_rand(0, 2)]; $message = $messages[mt_rand(0, 2)];
?> ?>
<h1><?php echo $message; ?></h1> <h1 class="laravel"><?php echo $message; ?></h1>
<div id="content">
An error occured while we were processing your request.
<br /><br />
Would you like go to our <a href="<?php echo System\Config::get('application.url'); ?>">home page</a> instead?
</div> </div>
<div id="wrapper">
<?php
$apologies = array("It's not your fault.", "Don't give up on us.", "We're really sorry.");
$apology = $apologies[mt_rand(0, 2)];
?>
<h2><?php echo $apology; ?></h2>
<p>Something failed while we were handling your request. Would you like go to our <a href="<?php echo System\Config::get('application.url'); ?>">home page</a> instead?</p>
</div> </div>
</body> </body>
</html> </html>
\ No newline at end of file
## Authentication Configuration
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:
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".
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**:
'by_id' => function($id)
{
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.
'by_username' => function($username)
{
return User::where('email', '=', $username)->first();
}
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
## Authentication Usage
- [Salting & Hashing](#hash)
- [Logging In](#login)
- [Protecting Routes](#filter)
- [Retrieving The Logged In User](#user)
- [Logging Out](#logout)
> **Note:** Before using the Auth class, you must [specify a session driver](/docs/session/config).
<a name="hash"></a>
### 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.
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:
$password = Hash::make('secret');
The **make** method of the Hash class will return a 60 character hashed string.
You can compare an unhashed value against a hashed one using the **check** method on the **Hash** class:
if (Hash::check('secret', $hashed_value))
{
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.
<a name="login"></a>
### 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:
if (Auth::login('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.
To determine if the user of your application is logged in, call the **check** method:
if (Auth::check())
{
return "You're logged in!";
}
<a name="filter"></a>
### Protecting Routes
It is common to limit access to certain routes only to logged in users. It's a breeze in Laravel using the built-in [auth filter](/docs/start/routes#filters). If the user is logged in, the request will proceed as normal; however, if the user is not logged in, they will be redirected to the "login" [named route](/docs/start/routes#named).
To protect a route, simply attach the **auth** filter:
'GET /admin' => array('before' => 'auth', 'do' => function() {})
> **Note:** You are free to edit the **auth** filter however you like. A default implementation is located in **application/filters.php**.
<a name="user"></a>
### Retrieving The Logged In User
Once a user has logged in to your application, you may easily access the user model via the **user** method on the Auth class:
return Auth::user()->email;
> **Note:** If the user is not logged in, the **user** method will return NULL.
<a name="logout"></a>
### Logging Out
Ready to log the user out of your application? It's simple:
Auth::logout();
This method will remove the user ID from the session, and the user will no longer be considered logged in on subsequent requests to your application.
\ No newline at end of file
## Cache Configuration
- [Memcached](#memcached)
- [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:
- File System
- Memcached
- APC
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.
> **Note:** Before using the file system cache driver, make sure your **application/storage/cache** directory is writeable.
<a name="memcached"></a>
### Memcached
[Memcached](http://memcached.org) is an ultra-fast, open-source distributed memory object caching system used by sites such as Wikipedia and Facebook. Before using Laravel's Memcached driver, you will need to install and configure Memcached and the PHP Memcache extension on your server.
Once Memcached is installed on your server, configuring the Laravel driver is a breeze. First, set the **driver** in the **application/config/cache.php** file:
'driver' => 'memcached'
Next, add your Memcached servers to the **servers** array:
'servers' => array(
array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
)
<a name="keys"></a>
### 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:
'key' => 'laravel'
\ No newline at end of file
## Cache Usage
- [Storing Items](#put)
- [Retrieving Items](#get)
- [Removing Items](#forget)
<a name="put"></a>
### Storing Items
Storing items in the cache is simple. Simply call the **put** method on the Cache class:
Cache::put('name', 'Taylor', 10);
The first parameter is the **key** to the cache item. You will use this key to retrieve the item from the cache. The second parameter is the **value** of the item. The third parameter is the number of **minutes** you want the item to be cached.
> **Note:** It is not necessary to serialize objects when storing them in the cache.
<a name="get"></a>
### Retrieving Items
Retrieving items from the cache is even more simple than storing them. It is done using the **get** method. Just mention the key of the item you wish to retrieve:
$name = Cache::get('name');
By default, NULL will be returned if the cached item has expired or does not exist. However, you may pass a different default value as a second parameter to the method:
$name = Cache::get('name', 'Fred');
Now, "Fred" will be returned if the "name" cache item has expired or does not exist.
What if you need a value from your database if a cache item doesn't exist? The solution is simple. You can pass a closure into the **get** method as a default value. The closure will only be executed if the cached item doesn't exist:
$users = Cache::get('count', function() {return DB::table('users')->count();});
Let's take this example a step further. Imagine you want to retrieve the number of registered users for your application; however, if the value is not cached, you want to store the default value in the cache. It's a breeze using the **remember** method:
$users = Cache::remember('count', function() {return DB::table('users')->count();}, 5);
Let's talk through that example. If the **count** item exists in the cache, it will be returned. If it doesn't exist, the result of the closure will be stored in the cache for five minutes **and** be returned by the method. Slick, huh?
Laravel even gives you a simple way to determine if a cached item exists using the **has** method:
if (Cache::has('name'))
{
$name = Cache::get('name');
}
<a name="forget"></a>
### Removing Items
Need to get rid of a cached item? No problem. Just mention the name of the item to the **forget** method:
Cache::forget('name');
\ No newline at end of file
## Getting Started
- [Requirements & Installation](/docs/start/install)
- [Basic Configuration](/docs/start/config)
- [Routes](/docs/start/routes)
- [Defining Routes](/docs/start/routes#define)
- [Wildcard URI Segments](/docs/start/routes#segments)
- [Named Routes](/docs/start/routes#named)
- [Route Filters](/docs/start/routes#filters)
- [Organizing Routes](/docs/start/routes#organize)
- [Views & Responses](/docs/start/views)
- [Creating Views](/docs/start/views#create)
- [Binding Data To Views](/docs/start/views#bind)
- [Nesting Views Within Views](/docs/start/views#nest)
- [Redirects](/docs/start/views#redirect)
- [Downloads](/docs/start/views#downloads)
- [Building URLs](/docs/start/views#urls)
- [Building HTML](/docs/start/views#html)
- [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)
- [Data Validation](/docs/start/validation)
## Database
- [Configuration](/docs/database/config)
- [Usage](/docs/database/usage)
- [Fluent Query Builder](/docs/database/query)
- [Eloquent ORM](/docs/database/eloquent)
## Caching
- [Configuration](/docs/cache/config)
- [Usage](/docs/cache/usage)
## Sessions
- [Configuration](/docs/session/config)
- [Usage](/docs/session/usage)
## Authentication
- [Configuration](/docs/auth/config)
- [Usage](/docs/auth/usage)
## Other Topics
- [Working With Files](/docs/other/file)
- [Localization](/docs/other/lang)
- [Encryption](/docs/other/crypt)
- [Benchmarking Code](/docs/other/benchmark)
\ No newline at end of file
## Database Configuration
- [Quick Start Using SQLite](#quick)
- [Configuring MySQL or PostgreSQL](#server)
- [Setting The Default Connection Name](#default)
Database configuration in Laravel is easy. The hardest part is deciding which database to use. Three popular open-source databases are supported out of the box:
- MySQL
- PostgreSQL
- SQLite
All of the database configuration options live in the **application/config/db.php** file. Let's get started.
<a name="quick"></a>
### Quick Start Using SQLite
[SQLite](http://sqlite.org) is an awesome, zero-configuration database system. By default, Laravel is configured to use a SQLite database. Really, you don't have to change anything. Just drop a SQLite database named **application.sqlite** into the **application/storage/db directory**. You're done.
Of course, if you want to name your database something besides "application", you can modify the database option in the SQLite section of the **application/config/db.php** file:
'sqlite' => array(
'driver' => 'sqlite',
'database' => 'your_database_name',
)
If your application receives less than 100,000 hits per day, SQLite should be suitable for production use in your application. Otherwise, consider using MySQL or PostgreSQL.
> **Note:** Need a good SQLite manager? Check out this [Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/).
<a name="server"></a>
### Configuring MySQL or PostgreSQL
If you are using MySQL or PostgreSQL, you will need to edit the configuration options in **application/config/db.php**. Don't worry. In the configuration file, sample configurations exist for both systems. All you need to do is change the options as necessary for your server and set the default connection name.
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => 'password',
'charset' => 'utf8',
),
<a name="default"></a>
### Setting The Default Connection Name
As you have probably noticed, each database connection defined in the **application/config/db.php** file has a name. By default, there are three connections defined: **sqlite**, **mysql**, and **pgsql**. You are free to change these connection names. The default connection can be specified via the **default** option:
'default' => 'sqlite';
The default connection will always be used by the [fluent query builder](/docs/database/query) and [Eloquent ORM](/docs/database/eloquent). If you need to change the default connection during a request, use the **Config::set** method.
\ No newline at end of file
This diff is collapsed.
## Fluent Query Builder
- [Retrieving Records](#get)
- [Building Where Clauses](#where)
- [Dynamic Where Clauses](#dynamic)
- [Table Joins](#joins)
- [Ordering Results](#ordering)
- [Skip & Take](#limit)
- [Aggregates](#aggregates)
- [Inserting Records](#insert)
- [Updating Records](#update)
- [Deleting Records](#delete)
Laravel provides an awesome, easy-to-use fluent interface for building SQL queries and working with your database. All queries use prepared statements and are protected against SQL injection. Working with your database doesn't have to be a headache.
You can begin a fluent query using the **table** method on the DB class. Just mention the table you wish to query:
$query = DB::table('users');
You now have a fluent query builder for the "users" table. Using this query builder, you can retrieve, insert, update, or delete records from the table.
<a name="get"></a>
### Retrieving Records
There are two methods available for retrieving records using a fluent query: **get** and **first**. The **get** method will return an array of records from your database. Each record will be an object with properties corresponding to the columns of the table:
$users = DB::table('users')->get();
foreach ($users as $user)
{
echo $user->email;
}
Instead of returning an array, the **first** method will return a single object:
$user = DB::table('users')->first();
echo $user->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'));
Need to get distinct records from the database? It's easy. Call the **distinct** method before retrieving your records:
$user = DB::table('users')->distinct()->get();
> **Note:** If no results are found, the **first** method will return NULL. The **get** method will return an empty array.
<a name="where"></a>
### Building Where Clauses
#### where and or\_where
Building WHERE clauses in Laravel is painless. There are a variety of methods to assist you. The most basic of these methods are the **where** and **or_where** methods. Here is how to use them:
return DB::table('users')
->where('id', '=', 1)
->or_where('email', '=', 'example@gmail.com')
->first();
Of course, you are not limited to simply checking equality. You may also use **greater-than**, **less-than**, **not-equal**, and **like**:
return DB::table('users')
->where('id', '>', 1)
->or_where('name', 'LIKE', '%Taylor%')
->first();
You may have assumed that the **where** method will add to the query using an AND condition, while the **or_where** method will use an OR condition. You assumed correctly.
#### where\_in, where\_not\_in, or\_where\_in, and or\_where\_not\_in
The suite of **where_in** methods allows you to easily construct queries that search an array of values:
DB::table('users')->where_in('id', array(1, 2, 3))->get();
DB::table('users')->where_not_in('id', array(1, 2, 3))->get();
DB::table('users')
->where('email', '=', 'example@gmail.com')
->or_where_in('id', array(1, 2, 3))
->get();
DB::table('users')
->where('email', '=', 'example@gmail.com')
->or_where_not_in('id', array(1, 2, 3))
->get();
#### where\_null, where\_not\_null, or\_where\_null, and or\_where\_not\_null
The suite of **where_null** methods makes checking for NULL values a piece of cake:
return DB::table('users')->where_null('updated_at')->get();
return DB::table('users')->where_not_null('updated_at')->get();
return DB::table('users')
->where('email', '=', 'example@gmail.com')
->or_where_null('updated_at')
->get();
return DB::table('users')
->where('email', '=', 'example@gmail.com')
->or_where_not_null('updated_at')
->get();
<a name="dynamic"></a>
### Dynamic Where Clauses
Ready for some really beautiful syntax? Check out **dynamic where methods**:
$user = DB::table('users')->where_email('example@gmail.com')->first();
$user = DB::table('users')->where_email_and_password('example@gmail.com', 'secret');
$user = DB::table('users')->where_id_or_name(1, 'Fred');
Aren't they a breathe of fresh air?
<a name="joins"></a>
### Table Joins
Need to join to another table? Try the **join** and **left\_join** methods:
DB::table('users')
->join('phone', 'users.id', '=', 'phone.user_id')
->get(array('users.email', 'phone.number'));
The **table** you wish to join is passed as the first parameter. The remaining three parameters are used to construct the **ON** clause of the join.
Once you know how to use the join method, you know how to **left_join**. The method signatures are the same:
DB::table('users')
->left_join('phone', 'users.id', '=', 'phone.user_id')
->get(array('users.email', 'phone.number'));
<a name="ordering"></a>
### Ordering Results
You can easily order the results of your query using the **order_by** method. Simply mention the column and direction (desc or asc) of the sort:
return DB::table('users')->order_by('email', 'desc')->get();
Of course, you may sort on as many columns as you wish:
return DB::table('users')
->order_by('email', 'desc')
->order_by('name', 'asc')
->get();
<a name="limit"></a>
### Skip & Take
If you would like to **LIMIT** the number of results returned by your query, you can use the **take** method:
return DB::table('users')->take(10)->get();
To set the **OFFSET** of your query, use the **skip** method:
return DB::table('users')->skip(10)->get();
<a name="aggregates"></a>
### Aggregates
Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the query:
$min = DB::table('users')->min('age');
$max = DB::table('users')->max('weight');
$avg = DB::table('users')->avg('salary');
$sum = DB::table('users')->sum('votes');
$count = DB::table('users')->count();
Of course, you may wish to limit the query using a WHERE clause first:
$count = DB::table('users')->where('id', '>', 10)->count();
<a name="insert"></a>
### Inserting Records
Inserting records is amazingly easy using the **insert** method. The method only expects an array of values to insert. It couldn't be simpler. The insert method will simply return true or false, indicating whether the query was successful:
DB::table('users')->insert(array('email' => 'example@gmail.com'));
Inserting a record that has an auto-incrementing ID? You can use the **insert\_get\_id** method to insert a record and retrieve the ID:
$id = DB::table('users')->insert_get_id(array('email' => 'example@gmail.com'));
> **Note:** The **insert\_get\_id** method expects the name of the auto-incrementing column to be "id".
<a name="update"></a>
### Updating Records
Updating records is just as simple as inserting them. Simply pass an array of values to the **update** method:
$affected = DB::table('users')->update(array('email' => 'new_email@gmail.com'));
Of course, when you only want to update a few records, you should add a WHERE clause before calling the update method:
$affected = DB::table('users')
->where('id', '=', 1)
->update(array('email' => 'new_email@gmail.com'));
<a name="delete"></a>
### Deleting Records
When you want to delete records from your database, simply call the **delete** method:
$affected = DB::table('users')->where('id', '=', 1)->delete();
Want to quickly delete a record by its ID? No problem. Just pass the ID into the delete method:
$affected = DB::table('users')->delete(1);
\ No newline at end of file
## Database Usage
### Queries
Running queries against a database connection is a breeze using the **query** method on the DB class:
$users = DB::query('select * from users');
The **query** method also allows you to specify bindings for your query in the second parameter to the method:
$users = DB::query('select * from users where name = ?', array('test'));
The return value of the query method depends on the type of query that is executed:
- **SELECT** statements will return an array of stdClass objects with properties corresponding to each column on the table.
- **INSERT** statements will return **true** or **false**, depending on the success of the query.
- **UPDATE** and **DELETE** statements will return the number of rows affected by the query.
### 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:
$pdo = DB::connection('sqlite');
> **Note:** If no connection name is specified, the **default** connection will be returned.
### Driver
Want to know which PDO driver is being used for a connection? Check out the **driver** method:
$driver = DB::driver('connection_name');
> **Note:** If no connection name is specified, the **default** connection driver will be returned.
\ No newline at end of file
## Benchmarking Code
- [The Basics](#basics)
- [Using Timers](#timers)
- [Checking Memory Usage](#memory)
<a name="basics"></a>
### The Basics
When making changes to your code, it's helpful to know the performance impact of your changes. Laravel provides a simple class to help you time code execution and check memory consumption. It's called the **Benchmark** class and it's a breeze to use.
<a name="timers"></a>
### Using Timers
To start a timer, simply call the **start** method on the Benchmark class and give your timer a name:
Benchmark::start('foo');
Pretty easy, right?
You can easily check how much time has elapsed (in milliseconds) using the **check** method. Again, just mention the name of the timer to the method:
echo Benchmark::check('foo');
<a name="memory"></a>
### Checking Memory Usage
Need to know how much memory is being used by your application? No problem. Just call the **memory** method to get your current memory usage in megabytes:
echo Benchmark::memory();
\ No newline at end of file
## Encryption
- [The Basics](#basics)
- [Encrypting A String](#encrypt)
- [Decrypting A String](#decrypt)
<a name="basics"></a>
### The Basics
Need to do secure, two-way encryption? Laravel has you covered with the **Crypt** class. The Crypt class provides strong AES-256 encryption and decryption out of the box via the Mcrypt PHP extension.
To get started, you must set your **application key** in the **application/config/application.php** file. This key should be very random and very secret, as it will be used during the encryption and decryption process. It is best to use a random, 32 character alpha-numeric string:
'key' => 'xXSAVghP7myRo5xqJAnMvQwBc7j8qBZI';
Wonderful. You're ready to start encrypting.
> **Note:** Don't forget to install the Mcrypt PHP extension on your server.
<a name="encrypt"></a>
### Encrypting A String
Encrypting a string is a breeze. Just pass it to the **encrypt** method on the Crypt class:
Crypt::encrypt($value);
Do you feel like James Bond yet?
<a name="decrypt"></a>
### Decrypting A String
So you're ready to decrypt a string? It's simple. Just use the **decrypt** method on the Crypt class:
Crypt::decrypt($encrypted_value);
> **Note:** The decrypt method will only decrypt strings that were encrypted using **your** application key.
\ No newline at end of file
## Working With Files
- [Reading Files](#get)
- [Writing Files](#put)
- [File Uploads](#upload)
- [File Extensions](#ext)
- [Checking File Types](#is)
- [Getting MIME Types](#mime)
<a name="get"></a>
### Reading Files
It's a breeze to get the contents of a file using the **get** method on the **File** class:
$contents = File::get('path/to/file');
<a name="put"></a>
### Writing Files
Need to write to a file? Check out the **put** method:
File::put('path/to/file', 'file contents');
Want to append to the file instead of overwriting the existing contents? No problem. Use the **append** method:
File::append('path/to/file', 'appended file content');
<a name="upload"></a>
### File Uploads
After a file has been uploaded to your application, you will want to move it from its temporary location to a permanent directory. You can do so using the **upload** method. Simply mention the **name** of the uploaded file and the path where you wish to store it:
File::upload('picture', 'path/to/pictures');
> **Note:** You can easily validate file uploads using the [Validator class](/docs/start/validation).
<a name="ext"></a>
### File Extensions
Need to get the extension of a file? Just pass the filename to the **extension** method:
File::extension('picture.png');
<a name="is"></a>
### Checking File Types
Often, it is important to know the type of a file. For instance, if a file is uploaded to your application, you may wish to verify that it is an image. It's easy using the **is** method on the **File** class. Simply pass the extension of the file type you are expecting. Here's how to verify that a file is a JPG image:
if (File::is('jpg', 'path/to/file.jpg'))
{
//
}
The **is** method does not simply check the file extension. The Fileinfo PHP extension will be used to read the content of the file and determine the actual MIME type. Pretty cool, huh?
> **Note:** You may pass any of the extensions defined in the **application/config/mimes.php** file to the **is** method.
<a name="mime"></a>
### Getting MIME Types
Need to know the MIME type associated with a file extension? Check out the **mime** method:
echo File::mime('gif');
The statement above returns the following string:
image/gif
> **Note:** This method simply returns the MIME type defined for the extension in the **application/config/mimes.php** file.
\ No newline at end of file
## Localization
- [The Basics](#basics)
- [Retrieving A Language Line](#get)
- [Place Holders & Replacements](#replace)
<a name="basics"></a>
### The Basics
Localization is the process of translating your application into different languages. The **Lang** class provides a simple mechanism to help you organize and retrieve the text of your multilingual application.
All of the language files for your application live under the **application/lang** directory. Within the **application/lang** directory, you should create a directory for each language your application speaks. So, for example, if your application speaks English and Spanish, you might create **en** and **sp** directories under the **lang** directory.
Each language directory may contain many different language files. Each language file is simply an array of string values in that language. In fact, language files are structured identically to configuration files. For example, within the **application/lang/en** directory, you could create a **marketing.php** file that looks like this:
return array(
'welcome' => 'Welcome to our website!',
);
Next, you should create a corresponding **marketing.php** file within the **application/lang/sp** directory. The file would look something like this:
return array(
'welcome' => 'Bienvenido a nuestro sitio web!',
);
Nice! Now you know how to get started setting up your language files and directories. Let's keep localizing!
<a name="basics"></a>
### Retrieving A Language Line
To retrieve a language line, first create a Lang instance using the **line** method, then call the **get** method on the instance:
echo Lang::line('marketing.welcome')->get();
Notice how a dot was used to separate "marketing" and "welcome"? The text before the dot corresponds to the language file, while the text after the dot corresponds to a specific string within that file.
But, how did the method know which language directory to retrieve the message from? By default, the **get** method will use the language specified in your **application/config/application.php** configuration file. In this file you may set the default language of your application using the **language** option:
'language' => 'en'
Need to retrieve the line in a language other than your default? Not a problem. Just mention the language to the **get** method:
echo Lang::line('marketing.welcome')->get('sp');
<a name="replace"></a>
### Place Holders & Replacements
Now, let's work on our welcome message. "Welcome to our website!" is a pretty generic message. It would be helpful to be able to specify the name of the person we are welcoming. But, creating a language line for each user of our application would be time-consuming and ridiculous. Thankfully, you don't have to. You can specify "place-holders" within your language lines. Place-holders are preceeded by a colon:
'welcome' => 'Welcome to our website, :name!'
Then, simply pass an array of place-holder replacements to the **replace** method on a Lang instance:
echo Lang::line('marketing.welcome')->replace(array('name' => 'Taylor'))->get();
This statement will return a nice, heart-warming welcome message:
Welcome to our website, Taylor!
\ No newline at end of file
<a name="config"></a>
## Session Configuration
- [File System Sessions](#file)
- [Database Sessions](#database)
- [Memcached Sessions](#memcached)
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:
- File System
- Database
- Memcached
<a name="file"></a>
### File System Sessions
Most likely, your application will work great using file system sessions. However, if your application receives heavy traffic or runs on a server farm, use database or Memcached sessions.
To get started using file system sessions, just set the driver option in the **application/config/session.php** file:
'driver' => 'file'
That's it. You're ready to go!
> **Note:** File system sessions are stored in the **application/storage/sessions** directory, so make sure it's writeable.
<a name="database"></a>
### Database Sessions
To start using database sessions, you will first need to [configure your database connection](/docs/database/config).
Already setup your database? Nice! Next, you will need to create a session table. Here are some SQL statements to help you get started:
#### SQLite
CREATE TABLE "sessions" (
"id" VARCHAR PRIMARY KEY NOT NULL UNIQUE,
"last_activity" INTEGER NOT NULL,
"data" TEXT NOT NULL
);
#### MySQL
CREATE TABLE `sessions` (
`id` VARCHAR(40) NOT NULL,
`last_activity` INT(10) NOT NULL,
`data` TEXT NOT NULL,
PRIMARY KEY (`id`)
);
If you would like to use a different table name, simply change the **table** option in the **application/config/session.php** file:
'table' => 'sessions'
Great! All you need to do now is set the driver in the **application/config/session.php** file:
'driver' => 'db'
<a name="memcached"></a>
### Memcached Sessions
Before using Memcached sessions, you must [configure your Memcached servers](/docs/cache/config#memcached).
All done? Great! Just set the driver in the **application/config/session.php** file:
'driver' => 'memcached'
\ No newline at end of file
## Session Usage
- [Storing Items](#put)
- [Retrieving Items](#get)
- [Removing Items](#forget)
- [Regeneration](#regeneration)
<a name="put"></a>
### Storing Items
Storing items in the session is a breeze. Simply call the put method on the Session class:
Session::put('name', 'Taylor');
The first parameter is the **key** to the session item. You will use this key to retrieve the item from the session. The second parameter is the **value** of the item.
Need to store an item in the session that should expire after the next request? Check out the **flash** method. It provides an easy way to store temporary data like status or error messages:
Session::flash('status', 'Welcome Back!');
<a name="get"></a>
### Retrieving Items
Retrieving items from the session is no problem. You can use the **get** method on the Session class to retrieve any item in the session, including flash data. Just pass the key of the item you wish to retrieve:
$name = Session::get('name');
By default, NULL will be returned if the session item does not exist. However, you may pass a default value as a second parameter to the get method:
$name = Session::get('name', 'Fred');
$name = Session::get('name', function() {return 'Fred';});
Now, "Fred" will be returned if the "name" item does not exist in the session.
Laravel even provides a simple way to determine if a session item exists using the **has** method:
if (Session::has('name'))
{
$name = Session::get('name');
}
<a name="forget"></a>
### Removing Items
Need to get rid of a session item? No problem. Just mention the name of the item to the **forget** method on the Session class:
Session::forget('name');
You can even remove all of the items from the session using the **flush** method:
Session::flush();
<a name="regeneration"></a>
### Regeneration
Sometimes you may want to "regenerate" the session ID. This simply means that a new, random session ID will be assigned to the session. Here's how to do it:
Session::regenerate();
\ No newline at end of file
## Basic Configuration
- [Quick Start](#quick)
- [Cleaner URLs](#clean)
- [Errors & Logging](#errors)
<a name="quick"></a>
### Quick Start
When starting a new project, you shouldn't be bombarded with loads of confusing configuration decisions. For that reason, Laravel is intelligently configured out of the box. The **application/config/application.php** file contains the basic configuration options for your application.
There is only one option that **must** be set when starting a new application. Laravel needs to know the URL you will use to access your application. Simply set the url in the **application/config/application.php** file:
'url' => 'http://localhost';
> **Note:** If you are using mod_rewrite, you should set the index option to an empty string.
<a name="clean"></a>
### Cleaner URLs
Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
Is the .htaccess file above not working for you? Try this one:
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string.
> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file.
<a name="errors"></a>
### Errors & Logging
- [404 Errors](#error-404)
- [Error Detail](#error-detail)
- [Logging](#error-logging)
<a name="error-404"></a>
#### 404 Errors
When a request is made to your application that cannot be matched to a route, the 404 error view will be sent to the browser. This view lives in **application/views/error/404.php** and you are free to modify it however you wish.
<a name="error-detail"></a>
#### Error Detail
You can easily control the level of error detail via the **detail** option in the **application/config/errors.php** file.
'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.
> **Note:** In a production environment, it is strongly suggested that you turn off error details.
<a name="error-logging"></a>
#### 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:
'log' => true;
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
## Requirements & Installation
### Requirements
- Apache, nginx, or another compatible web server.
- PHP 5.3+.
### Installation
1. [Download Laravel](https://github.com/taylorotwell/laravel/zipball/master)
2. Extract the Laravel archive and upload the contents to your web server.
4. Set the URL of your application in the **application/config/application.php** file.
5. Navigate to your application in a web browser.
If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn!
### Extras
Installing the following goodies will help you take full advantage of Laravel, but they are not required:
- SQLite, MySQL, or PostgreSQL PDO drivers.
- [Memcached](http://memcached.org) or APC.
### Problems?
- Make sure the **public** directory is the document root of your web server.
- If you are using mod_rewrite, set the **index** option in **application/config/application.php** to an empty string.
\ No newline at end of file
## Interaction
- [Input](/docs/start/interaction#basics)
- [Old Input](/docs/start/interaction#old)
- [Cookies](/docs/start/interaction#cookies)
- [Building Forms](/docs/start/interaction#forms)
All web applications receive input via HTTP requests. The input can be sent to your application via any of the four HTTP verbs: **GET**, **POST**, **PUT**, or **DELETE**. Input can also be sent to your application via cookies, which can store small amounts of information and are stored on the user's computer.
Let's dig into the classes Laravel provides for working with user input!
> **Note:** Laravel doesn't mess with your query strings. Feel free to use them.
<a name="basics"></a>
## Input
The **Input** class handles input that comes into your application via GET, POST, PUT, or DELETE requests. Retrieving input using the Input class is effortless. Just use the **get** method:
$email = Input::get('email');
> **Note:** The get method is used for all request types, not just GET requests. You may use it on POST, PUT, and DELETE requests as well.
By default, NULL will be returned if the input item does not exist. However, you may pass a different default value as a second parameter to the method:
$name = Input::get('name', 'Fred');
Now, "Fred" will be returned if the "name" input item does not exist. You may even pass a closure as a default value:
$name = Input::get('name', function() {return 'Fred';});
Need to determine if an input item exists? Use the **has** method:
if (Input::has('name'))
{
$name = Input::get('name');
}
> **Note:** The **has** method will return **false** if the input item exists but is an empty string.
Need to access the **$_FILES** array? It's easy using the **file** method:
$picture = Input::file('picture');
$size = Input::file('picture.size');
Sometimes you may need to merge the input and $_FILES array. Check out the **all** method:
$input = Input::all();
<a name="old"></a>
## Old Input
Have you ever tried to re-populate an input form after an invalid form submission? It can get pretty clunky. Not in Laravel. You can easily retrieve the input from the previous request using the **old** method on the Input class:
$name = Input::old('name');
> **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:
$name = Input::old('name', 'Fred');
Once again, there is a simple way to determine if an old input item exists using the **had** method:
if (Input::had('name'))
{
$name = Input::old('name');
}
<a name="cookies"></a>
## Cookies
The **Cookie** class provides simple functions for retrieving, setting, and deleting cookies.
To retrieve a cookie value, simply mention its name to the **get** method:
$name = Cookie::get('name');
Of course, just like the Input class, you may pass a default value in the second parameter to the **get** method:
$name = Cookie::get('name', 'Fred');
Also just like the Input class, the Cookie class has a simple method to determine if a cookie exists:
if (Cookie::has('name'))
{
$name = Cookie::get('name');
}
Need to create a cookie? No problem. Check out the **put** method:
Cookie::put('name', 'Fred', 60);
The put method accepts almost the exact same parameters as the PHP setcookie method. However, just pass the number of **minutes** you want the cookie to live as the third parameter. You don't have to worry about any clunky expiration date calculations.
If you need to create a "permanent" cookie, try the **forever** method. It creates a cookie that lives for five years:
Cookie::forever('name', 'Fred');
To delete a cookie, use the **forget** method:
Cookie::forget('name');
<a name="forms"></a>
## Building Forms
- [Opening A Form](#form-open)
- [CSRF Protection](#form-csrf)
- [Labels](#form-labels)
- [Text, Text Area, Password & Hidden Fields](#form-text)
- [Checkboxes & Radio Buttons](#form-check)
- [Drop-Down Lists](#form-lists)
- [Buttons](#form-buttons)
Almost every web application receives input through HTML forms. As you have probably already learned, Laravel is here to make your life easier. That's why generating forms using the **Form** class is a breeze.
> **Note:** All input data displayed in elements generated by the **Form** class is filtered through the HTML::entities method.
<a name="form-open"></a>
### Opening A Form
Opening a form is simple. Just call the **open** method on the Form class:
echo Form::open();
When called without any parameters, the open method will create a form that will POST to the current URL. However, you'll probably want to point your forms to other URLs too. No problem. Just mention the URL to the method. You can even specify the request method (GET, POST, PUT, or DELETE) in the second parameter to the method:
echo Form::open('user/profile', 'PUT');
Need to apply a class or other attribute to the form tag generated by the open method? Simply pass an array of attributes as a third parameter:
echo Form::open('user/profile', 'PUT', array('class' => 'awesome'));
> **Note:** The open method automatically prepares your form to receive UTF-8 input.
Need a form that can handle file uploads? Use the **open_for_files** method:
echo Form::open_for_files('user/profile');
<a name="form-csrf"></a>
### CSRF Protection
Laravel provides an easy method of protecting your application from [cross-site request forgeries](http://en.wikipedia.org/wiki/Cross-site_request_forgery). First, a random token is placed in your user's session. Don't sweat it, this is done automatically. Next, use the **token** method to generate a hidden form input field containing the random token on your form:
echo Form::token();
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:
echo Form::raw_token();
> **Note:** Don't forget to [specify a session driver](/docs/session/config) before using these methods.
<a name="form-labels"></a>
### Labels
Need to generate a label for a form element? It's simple using the **label** method. Just pass the label name and display value to the method:
echo Form::label('email', 'E-Mail Address');
Of course, you may pass any attributes you wish in the third parameter to the method:
echo Form::label('email', 'E-Mail Address', array('class' => 'awesome'));
> **Note:** After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well.
<a name="form-text"></a>
### Text, Text Area, Password & Hidden Fields
Generating text boxes couldn't be easier. Just call the **text** method on the Form class and mention the name of the field:
echo Form::text('username');
Already have a value you want to put in the text box? Throw it in as a second parameter:
echo Form::text('email', 'example@gmail.com');
Again, any other attributes you wish to apply to the text box may be passed in an array as the third parameter:
echo Form::text('email', 'example@gmail.com', array('class' => 'awesome'));
> **Note:** The **password**, **hidden**, and **textarea** methods have the same signature as the text method. You just learned four methods for the price of one!
<a name="form-check"></a>
### Checkboxes & Radio Buttons
What website doesn't have a checkbox? Actually, this one doesn't! But, thankfully, generating them is simple using the **checkbox** method on the Form class. Just give the checkbox a name and a value:
echo Form::checkbox('remember', 'yes');
Of course, the example above will generate the following HTML:
<input type="checkbox" name="remember" value="yes">
Need to generate a "checked" checkbox? No problem. Simply pass **true** as the third parameter to the method:
echo Form::checkbox('remember', 'yes', true);
As always, you may specify any extra attributes that should be applied to the checkbox. Pass them as the fourth parameter to the method:
echo Form::checkbox('remember', 'yes', true, array('class' => 'awesome'));
> **Note:** The **radio** method has the same signature as the checkbox method. Two for one!
<a name="form-lists"></a>
### Drop-Down Lists
Generating drop-down lists can be a headache. Thankfully, Laravel makes it refreshingly simple using the **select** method on the Form class. All you need to do is give your list a name and an array of options:
echo Form::select('size', array('L' => 'Large', 'S' => 'Small'));
If you wish to set the selected item, just pass the value as the third parameter to the method:
echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S');
You may specify any other attributes that should be applied to the list in the fourth parameter to the method:
echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S', array('class' => 'awesome'));
<a name="form-buttons"></a>
### Buttons
Creating a submit button is a cinch. Use the **submit** method on the Form class:
echo Form::submit('Click Me!');
Again, any other attributes that should be applied to the button may be passed to the method:
echo Form::submit('Click Me!', array('class' => 'awesome'));
> **Note:** Need to create a button element? Try the **button** method. It has the same signature as submit.
\ No newline at end of file
## Routes
- [Defining Routes](/docs/start/routes#define)
- [Wildcard URI Segments](/docs/start/routes#segments)
- [Named Routes](/docs/start/routes#named)
- [Route Filters](/docs/start/routes#filters)
- [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.
<a name="define"></a>
## Defining Routes
All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method:
'GET /home' => function()
{
// Handles GET requests to http://example.com/index.php/home
},
You can easily define a route to handle requests to more than one URI. Just use commas:
'POST /, POST /home' => function()
{
// Handles POST requests to http://example.com and http://example.com/home
}
> **Note:** The routes.php file replaces the "controllers" found in most frameworks. Have a fat model and keep this file light and clean. Thank us later.
<a name="segments"></a>
## Wildcard URI Segments
Laravel makes matching wildcard URI segments a breeze using the **(:num)** and **(:any)** place-holders. Check out these routes:
'PUT /user/(:num)' => function($id) {}
'DELETE /user/(:any)' => function($username) {}
Laravel will automatically pass the value of the wildcard segment into your route function.
> **Note:** The **(:any)** place-holder matches letters, number, dashes, and underscores.
Want to make an URI segment optional? No problem. Just put a **?** in the place-holder:
'GET /download/(:any?)' => function($branch = 'master') {}
If you need more power and precision (or just want to be extra nerdy), you can even use regular expressions:
'GET /product/([0-9]+)' => function($id) {}
<a name="named"></a>
## Named Routes
Once you start using named routes, you won't be able to live without them. They are that great. Here's how to do it:
'GET /user/login' => array('name' => 'login', 'do' => function() {})
Notice the route now has an array value with two keys: **name** and **do**. As you learned while studying filters, the **do** value is the method that will be executed by the route. As you have probably guessed, the **name** value is the name of the route.
Now that you have named the route, you can [generate URLs](/docs/start/views#urls) and [perform redirects](/docs/start/views#redirect) using the route name instead of the route URI. This means that you can change the route URI as much as you want and the links to that route on your views will always be correct. It's beautiful, isn't it?
<a name="filters"></a>
## Route Filters
Filters are methods that run before and after a request to your application. "Before" filters can even halt the request cycle by returning a response, providing an amazingly simple way to implement common tasks like redirecting a user to a login view. Let's dig in.
All filters are defined in the **application/filters.php** file. Intuitive, right? If you open the file, you will see that four filters have already been defined for you: **before**, **after**, **auth**, and **csrf**. The **before** and **after** filters are the two "global" filters. They are always executed on every request, regardless of the request method or URI.
All other filters must be attached to individual routes. Don't worry, you'll learn how to do this soon. The built-in **auth** and **csrf** filters handle two scenarios that are common to almost every web application: redirecting users to a login page and protecting against cross-site request forgeries.
### Defining Filters
To define your own filter, simply add it to the array in the **application/filters.php** file:
'my_filter' => function()
{
return 'Filtered!';
}
### Attaching Filters To Routes
Alright, ready to attach the filter to a route? Do it like this:
'GET /user' => array('before' => 'my_filter', 'do' => function()
{
//
})
Notice the route now has an array value with two keys: **before** and **do**. The **do** value is the method that will be executed by the route, while 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:
'POST /user' => array('before' => 'auth, csrf', 'do' => 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?
Of course, adding filters to run after the request is just as easy:
'my_filter' => function($response) {}
'GET /user' => array('after' => 'my_filter', 'do' => function() {})
> **Note:** "After" filters receive the response returned by the route function that handled the request.
<a name="organize"></a>
## Organizing Routes
So, you're building the next monolithic web application and your **application/routes.php** file is getting a little cramped? Don't worry, we have you covered.
Here's what to do. First, create an **application/routes** directory. Great! You're almost there. Now, just add route files to **application/routes** corresponding to the base URIs of your application. So, a **photo.php** file within **application/routes** would handle all requests to URIs beginning with **/photo**. Similarly, a **user.php** file handles all requests to URIs beginning with **/user**. For example, check out this **user.php** file:
<?php
return array(
'GET /user/profile/(:num)' => function($id)
{
return View::make('user/profile');
}
);
The **application/routes.php** file will continue to be loaded on every request, so any "catch-all" routes can still be placed in that file. The **application/routes.php** file should also still contain the route for the root of your application.
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -3,17 +3,12 @@ ...@@ -3,17 +3,12 @@
* Laravel - A clean and classy framework for PHP web development. * Laravel - A clean and classy framework for PHP web development.
* *
* @package Laravel * @package Laravel
* @version 1.1.1 * @version 1.2.0
* @author Taylor Otwell * @author Taylor Otwell
* @license MIT License * @license MIT License
* @link http://laravel.com * @link http://laravel.com
*/ */
// --------------------------------------------------------------
// Set the framework starting time.
// --------------------------------------------------------------
define('LARAVEL_START', microtime(true));
// -------------------------------------------------------------- // --------------------------------------------------------------
// Define the framework paths. // Define the framework paths.
// -------------------------------------------------------------- // --------------------------------------------------------------
...@@ -29,9 +24,10 @@ define('PACKAGE_PATH', APP_PATH.'packages/'); ...@@ -29,9 +24,10 @@ define('PACKAGE_PATH', APP_PATH.'packages/');
define('EXT', '.php'); define('EXT', '.php');
// -------------------------------------------------------------- // --------------------------------------------------------------
// Load the configuration class. // Load the classes used by the auto-loader.
// -------------------------------------------------------------- // --------------------------------------------------------------
require SYS_PATH.'config'.EXT; require SYS_PATH.'config'.EXT;
require SYS_PATH.'arr'.EXT;
// -------------------------------------------------------------- // --------------------------------------------------------------
// Register the auto-loader. // Register the auto-loader.
...@@ -39,9 +35,11 @@ require SYS_PATH.'config'.EXT; ...@@ -39,9 +35,11 @@ require SYS_PATH.'config'.EXT;
spl_autoload_register(require SYS_PATH.'loader'.EXT); spl_autoload_register(require SYS_PATH.'loader'.EXT);
// -------------------------------------------------------------- // --------------------------------------------------------------
// Set the error reporting level. // Set the error reporting and display levels.
// -------------------------------------------------------------- // --------------------------------------------------------------
error_reporting((System\Config::get('error.detail')) ? E_ALL | E_STRICT : 0); error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'Off');
// -------------------------------------------------------------- // --------------------------------------------------------------
// Register the error handlers. // Register the error handlers.
...@@ -49,13 +47,15 @@ error_reporting((System\Config::get('error.detail')) ? E_ALL | E_STRICT : 0); ...@@ -49,13 +47,15 @@ error_reporting((System\Config::get('error.detail')) ? E_ALL | E_STRICT : 0);
set_exception_handler(function($e) set_exception_handler(function($e)
{ {
require_once SYS_PATH.'error'.EXT; require_once SYS_PATH.'error'.EXT;
System\Error::handle($e); System\Error::handle($e);
}); });
set_error_handler(function($number, $error, $file, $line) set_error_handler(function($number, $error, $file, $line)
{ {
require_once SYS_PATH.'error'.EXT; require_once SYS_PATH.'error'.EXT;
System\Error::handle(new ErrorException($error, 0, $number, $file, $line));
System\Error::handle(new ErrorException($error, $number, 0, $file, $line));
}); });
register_shutdown_function(function() register_shutdown_function(function()
...@@ -63,7 +63,8 @@ register_shutdown_function(function() ...@@ -63,7 +63,8 @@ register_shutdown_function(function()
if ( ! is_null($error = error_get_last())) if ( ! is_null($error = error_get_last()))
{ {
require_once SYS_PATH.'error'.EXT; require_once SYS_PATH.'error'.EXT;
System\Error::handle(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
System\Error::handle(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
} }
}); });
...@@ -77,7 +78,7 @@ date_default_timezone_set(System\Config::get('application.timezone')); ...@@ -77,7 +78,7 @@ date_default_timezone_set(System\Config::get('application.timezone'));
// -------------------------------------------------------------- // --------------------------------------------------------------
if (System\Config::get('session.driver') != '') if (System\Config::get('session.driver') != '')
{ {
System\Session::load(); System\Session::load(System\Cookie::get('laravel_session'));
} }
// -------------------------------------------------------------- // --------------------------------------------------------------
...@@ -85,28 +86,14 @@ if (System\Config::get('session.driver') != '') ...@@ -85,28 +86,14 @@ if (System\Config::get('session.driver') != '')
// -------------------------------------------------------------- // --------------------------------------------------------------
$response = System\Route\Filter::call('before', array(), true); $response = System\Route\Filter::call('before', array(), true);
// -------------------------------------------------------------- // ----------------------------------------------------------
// Only execute the route function if the "before" filter did // Execute the route function.
// not override by sending a response. // ----------------------------------------------------------
// --------------------------------------------------------------
if (is_null($response)) if (is_null($response))
{ {
// ----------------------------------------------------------
// Route the request to the proper route.
// ----------------------------------------------------------
$route = System\Router::route(Request::method(), Request::uri()); $route = System\Router::route(Request::method(), Request::uri());
// ---------------------------------------------------------- $response = (is_null($route)) ? System\Response::make(View::make('error/404'), 404) : $route->call();
// Execute the route function.
// ----------------------------------------------------------
if ( ! is_null($route))
{
$response = $route->call();
}
else
{
$response = System\Response::make(View::make('error/404'), 404);
}
} }
else else
{ {
......
# Laravel - A Clean & Classy PHP Framework # Laravel - A Clean & Classy PHP Framework
### For more information, visit [http://laravel.com](http://laravel.com) ## Introduction
### For complete documentation, visit [http://docs.laravel.com](http://docs.laravel.com) Laravel is a clean and classy framework for PHP web development. Freeing you from spaghetti code, Laravel helps you create wonderful applications using simple, expressive syntax. Development should be a creative experience that you enjoy, not something that is painful. Enjoy the fresh air.
\ No newline at end of file
<a name="top">
## Table Of Contents
### Getting Started
- <a href="#installation">Requirements & Installation</a>
- <a href="#config">Basic Configuration</a>
- <a href="#routes">Routes</a>
- <a href="#views">Views & Responses</a>
- <a href="#urls">Generating URLs</a>
- <a href="#html">Generating HTML</a>
- <a href="#errors">Errors & Logs</a>
### Input
- <a href="#input">Retrieving Input</a>
- <a href="#cookie">Cookies</a>
- <a href="#validation">Validation</a>
- <a href="#forms">Building Forms</a>
### Database
- <a href="#db-config">Configuration</a>
- <a href="#db-usage">Usage</a>
- <a href="#fluent">Fluent Query Builder</a>
- <a href="#eloquent">Eloquent ORM</a>
### Caching
- <a href="#cache-config">Configuration</a>
- <a href="#cache-usage">Usage</a>
### Sessions
- <a href="#session-config">Configuration</a>
- <a href="#session-usage">Usage</a>
### Authentication
- <a href="#auth-config">Configuration</a>
- <a href="#auth-usage">Usage</a>
### Other Topics
- <a href="#lang">Localization</a>
- <a href="#crypt">Encryption</a>
<a name="installation"></a>
## Requirements & Installation
### Requirements
- Apache, nginx, or another compatible web server.
- PHP 5.3+ (which supports namespaces, closures, etc.)
### Installation
1. Download Laravel
2. Extract the Laravel archive and upload the contents to your web server.
3. Set the URL of your application in the **application/config/application.php** file.
4. Navigate to your application in a web browser.
If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn!
### Extras
Installaing the following goodies will help you take full advantage of Laravel, but they are not required:
- SQLite, MySQL, or PostgreSQL PDO drivers.
- Memcached or APC.
### Problems?
- Make sure the **public** directory is the document root of your web server.
- If you are using mod\_rewrite, set the **index** option in **application/config/application.php** to an empty string.
[Back To Top](#top)
<a name="config"></a>
## Basic Configuration
### Quick Start
When starting a new project, you shouldn't be bombarded with loads of confusing configuration decisions. For that reason, Laravel is intelligently configured out of the box. The **application/config/application.php** file contains the basic configuration options for your application.
There is only one option that **must** be set when starting a new application. Laravel needs to know the URL you will use to access your application. Simply set the url in the **application/config/application.php** file:
'url' => 'http://localhost';
> **Note:** If you are using mod_rewrite for cleaner URLs, you should set the index option to an empty string.
<a name="config-clean"></a>
### Cleaner URLs
Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
Is the .htaccess file above not working for you? Try this one:
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string.
> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file.
[Back To Top](#top)
<a name="routes"></a>
## Defining Routes
- [The Basics](#routes-basics)
- [Route URI Wildcards](#routes-wildcards)
- [Route Filters](#route-filters)
- [Named Routes](#routes-named)
- [Organizing Routes](#routes-folder)
<a name="routes-basics"></a>
### The Basics
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.
All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method:
'GET /home' => function()
{
// Handles GET requests to http://example.com/index.php/home
},
'PUT /user/update' => function()
{
// Handles PUT requests to http://example.com/index.php/user/update
}
You can easily define a route to handle requests to more than one URI. Just use commas:
'POST /, POST /home' => function()
{
// Handles POST requests to http://example.com and http://example.com/index.php/home
}
> **Note:** The routes.php file replaces the "controllers" found in most frameworks. Have a fat model and keep this file light and clean. Thank us later.
[Back To Top](#top)
<a name="routes-wildcards"></a>
### Route URI Wildcards
You can pass URI segments to your route functions using the **(:num)** and **(:any)** wildcards:
'PUT /user/(:num)' => function($id) {}
'GET /user/(:any)/edit' => function($username) {}
You may make segments optional by placing a **?** in the wildcard:
'GET /branch/(:any?)' => function($branch = 'master') {}
If you need more power and precision, you can even use regular expressions:
'GET /product/([0-9]+)' => function($id) {}
[Back To Top](#top)
\ No newline at end of file
...@@ -5,9 +5,11 @@ class Arr { ...@@ -5,9 +5,11 @@ class Arr {
/** /**
* Get an item from an array. * Get an item from an array.
* *
* If the specified key is null, the entire array will be returned.
*
* @param array $array * @param array $array
* @param string $key * @param string $key
* @param array $default * @param mixed $default
* @return mixed * @return mixed
*/ */
public static function get($array, $key, $default = null) public static function get($array, $key, $default = null)
...@@ -17,7 +19,12 @@ class Arr { ...@@ -17,7 +19,12 @@ class Arr {
return $array; return $array;
} }
return (array_key_exists($key, $array)) ? $array[$key] : $default; if (array_key_exists($key, $array))
{
return $array[$key];
}
return is_callable($default) ? call_user_func($default) : $default;
} }
} }
\ No newline at end of file
...@@ -29,28 +29,20 @@ class Auth { ...@@ -29,28 +29,20 @@ class Auth {
/** /**
* Get the current user of the application. * Get the current user of the application.
* *
* The user will be loaded using the user ID stored in the session.
*
* @return object * @return object
*/ */
public static function user() public static function user()
{ {
// -----------------------------------------------------
// Verify that sessions are enabled. Since the user ID
// is stored in the session, we can't authenticate
// without a session driver specified.
// -----------------------------------------------------
if (Config::get('session.driver') == '') if (Config::get('session.driver') == '')
{ {
throw new \Exception("You must specify a session driver before using the Auth class."); throw new \Exception("You must specify a session driver before using the Auth class.");
} }
$model = static::model();
// -----------------------------------------------------
// Load the user using the ID stored in the session.
// -----------------------------------------------------
if (is_null(static::$user) and Session::has(static::$key)) if (is_null(static::$user) and Session::has(static::$key))
{ {
static::$user = $model::find(Session::get(static::$key)); static::$user = call_user_func(Config::get('auth.by_id'), Session::get(static::$key));
} }
return static::$user; return static::$user;
...@@ -59,25 +51,17 @@ class Auth { ...@@ -59,25 +51,17 @@ class Auth {
/** /**
* Attempt to login a user. * Attempt to login a user.
* *
* If the user credentials are valid. The user ID will be stored in the session
* and will be considered "logged in" on subsequent requests to the application.
*
* @param string $username * @param string $username
* @param string $password * @param string $password
*/ */
public static function login($username, $password) public static function login($username, $password)
{ {
$model = static::model(); if ( ! is_null($user = call_user_func(Config::get('auth.by_username'), $username)))
$user = $model::where(Config::get('auth.username'), '=', $username)->first();
if ( ! is_null($user))
{ {
// ----------------------------------------------------- if (Hash::check($password, $user->password))
// Hash the password. If a salt is present on the user
// record, we will recreate the hashed password using
// the salt. Otherwise, we will just use a plain hash.
// -----------------------------------------------------
$password = (isset($user->salt)) ? Hash::make($password, $user->salt)->value : sha1($password);
if ($user->password === $password)
{ {
static::$user = $user; static::$user = $user;
...@@ -91,30 +75,15 @@ class Auth { ...@@ -91,30 +75,15 @@ class Auth {
} }
/** /**
* Logout the current user of the application. * Logout the user of the application.
* *
* @return void * @return void
*/ */
public static function logout() public static function logout()
{ {
// -----------------------------------------------------
// By removing the user ID from the session, the user
// will no longer be considered logged in on subsequent
// requests to the application.
// -----------------------------------------------------
Session::forget(static::$key); Session::forget(static::$key);
static::$user = null; static::$user = null;
} }
/**
* Get the authentication model.
*
* @return string
*/
private static function model()
{
return '\\'.Config::get('auth.model');
}
} }
\ No newline at end of file
...@@ -7,47 +7,111 @@ class Cache { ...@@ -7,47 +7,111 @@ class Cache {
* *
* @var Cache\Driver * @var Cache\Driver
*/ */
private static $drivers = array(); public static $drivers = array();
/** /**
* Get the cache driver instance. * All of the items retrieved by the cache drivers.
*
* @var array
*/
public static $items = array();
/**
* Get a cache driver instance. If no driver name is specified, the default
* cache driver will be returned as defined in the cache configuration file.
*
* Note: Cache drivers are managed as singleton instances.
* *
* @param string $driver * @param string $driver
* @return Cache\Driver * @return Cache\Driver
*/ */
public static function driver($driver = null) public static function driver($driver = null)
{
// --------------------------------------------------
// If the cache driver has already been instantiated,
// we'll just return that existing instance.
//
// Otherwise, we'll instantiate a new one.
// --------------------------------------------------
if ( ! array_key_exists($driver, static::$drivers))
{ {
if (is_null($driver)) if (is_null($driver))
{ {
$driver = Config::get('cache.driver'); $driver = Config::get('cache.driver');
} }
static::$drivers[$driver] = Cache\Factory::make($driver); if ( ! array_key_exists($driver, static::$drivers))
{
switch ($driver)
{
case 'file':
static::$drivers[$driver] = new Cache\Driver\File;
break;
case 'memcached':
static::$drivers[$driver] = new Cache\Driver\Memcached;
break;
case 'apc':
static::$drivers[$driver] = new Cache\Driver\APC;
break;
default:
throw new \Exception("Cache driver [$driver] is not supported.");
}
} }
return static::$drivers[$driver]; return static::$drivers[$driver];
} }
/**
* Get an item from the cache.
*
* @param string $key
* @param mixed $default
* @param string $driver
* @return mixed
*/
public static function get($key, $default = null, $driver = null)
{
if (isset(static::$items[$driver][$key]))
{
return static::$items[$driver][$key];
}
if (is_null($item = static::driver($driver)->get($key)))
{
return is_callable($default) ? call_user_func($default) : $default;
}
return static::$items[$driver][$key] = $item;
}
/**
* Get an item from the cache. If the item doesn't exist in the cache, store
* the default value in the cache and return it.
*
* @param string $key
* @param mixed $default
* @param int $minutes
* @param string $driver
* @return mixed
*/
public static function remember($key, $default, $minutes, $driver = null)
{
if ( ! is_null($item = static::get($key)))
{
return $item;
}
$default = is_callable($default) ? call_user_func($default) : $default;
static::driver($driver)->put($key, $default, $minutes);
return $default;
}
/** /**
* Pass all other methods to the default driver. * Pass all other methods to the default driver.
*
* Passing method calls to the driver instance provides a better API for the
* developer. For instance, instead of saying Cache::driver()->foo(), we can
* now just say Cache::foo().
*/ */
public static function __callStatic($method, $parameters) public static function __callStatic($method, $parameters)
{ {
// --------------------------------------------------
// Passing method calls to the driver instance allows
// a better API for the developer.
//
// For instance, instead of saying Cache::driver()->foo(),
// we can now just say Cache::foo().
// --------------------------------------------------
return call_user_func_array(array(static::driver(), $method), $parameters); return call_user_func_array(array(static::driver(), $method), $parameters);
} }
......
...@@ -14,10 +14,9 @@ interface Driver { ...@@ -14,10 +14,9 @@ interface Driver {
* Get an item from the cache. * Get an item from the cache.
* *
* @param string $key * @param string $key
* @param mixed $default
* @return mixed * @return mixed
*/ */
public function get($key, $default = null); public function get($key);
/** /**
* Write an item to the cache. * Write an item to the cache.
......
<?php namespace System\Cache\Driver; <?php namespace System\Cache\Driver;
class APC implements \System\Cache\Driver { use System\Config;
/** class APC implements \System\Cache\Driver {
* All of the loaded cache items.
*
* @var array
*/
private $items = array();
/** /**
* Determine if an item exists in the cache. * Determine if an item exists in the cache.
...@@ -24,24 +19,11 @@ class APC implements \System\Cache\Driver { ...@@ -24,24 +19,11 @@ class APC implements \System\Cache\Driver {
* Get an item from the cache. * Get an item from the cache.
* *
* @param string $key * @param string $key
* @param mixed $default
* @return mixed * @return mixed
*/ */
public function get($key, $default = null) public function get($key)
{
if (array_key_exists($key, $this->items))
{ {
return $this->items[$key]; return ( ! is_null($cache = apc_fetch(Config::get('cache.key').$key))) ? $cache : null;
}
$cache = apc_fetch(\System\Config::get('cache.key').$key);
if ($cache === false)
{
return $default;
}
return $this->items[$key] = $cache;
} }
/** /**
...@@ -54,7 +36,7 @@ class APC implements \System\Cache\Driver { ...@@ -54,7 +36,7 @@ class APC implements \System\Cache\Driver {
*/ */
public function put($key, $value, $minutes) public function put($key, $value, $minutes)
{ {
apc_store(\System\Config::get('cache.key').$key, $value, $minutes * 60); apc_store(Config::get('cache.key').$key, $value, $minutes * 60);
} }
/** /**
...@@ -65,7 +47,7 @@ class APC implements \System\Cache\Driver { ...@@ -65,7 +47,7 @@ class APC implements \System\Cache\Driver {
*/ */
public function forget($key) public function forget($key)
{ {
apc_delete(\System\Config::get('cache.key').$key); apc_delete(Config::get('cache.key').$key);
} }
} }
\ No newline at end of file
...@@ -2,13 +2,6 @@ ...@@ -2,13 +2,6 @@
class File implements \System\Cache\Driver { class File implements \System\Cache\Driver {
/**
* All of the loaded cache items.
*
* @var array
*/
private $items = array();
/** /**
* Determine if an item exists in the cache. * Determine if an item exists in the cache.
* *
...@@ -27,32 +20,23 @@ class File implements \System\Cache\Driver { ...@@ -27,32 +20,23 @@ class File implements \System\Cache\Driver {
* @param mixed $default * @param mixed $default
* @return mixed * @return mixed
*/ */
public function get($key, $default = null) public function get($key)
{ {
if (array_key_exists($key, $this->items))
{
return $this->items[$key];
}
if ( ! file_exists(APP_PATH.'storage/cache/'.$key)) if ( ! file_exists(APP_PATH.'storage/cache/'.$key))
{ {
return $default; return null;
} }
$cache = file_get_contents(APP_PATH.'storage/cache/'.$key); $cache = file_get_contents(APP_PATH.'storage/cache/'.$key);
// --------------------------------------------------
// Has the cache expired? The UNIX expiration time
// is stored at the beginning of the file.
// --------------------------------------------------
if (time() >= substr($cache, 0, 10)) if (time() >= substr($cache, 0, 10))
{ {
$this->forget($key); $this->forget($key);
return $default; return null;
} }
return $this->items[$key] = unserialize(substr($cache, 10)); return unserialize(substr($cache, 10));
} }
/** /**
......
<?php namespace System\Cache\Driver; <?php namespace System\Cache\Driver;
class Memcached implements \System\Cache\Driver { use System\Config;
/** class Memcached implements \System\Cache\Driver {
* All of the loaded cache items.
*
* @var array
*/
private $items = array();
/** /**
* Determine if an item exists in the cache. * Determine if an item exists in the cache.
...@@ -24,33 +19,11 @@ class Memcached implements \System\Cache\Driver { ...@@ -24,33 +19,11 @@ class Memcached implements \System\Cache\Driver {
* Get an item from the cache. * Get an item from the cache.
* *
* @param string $key * @param string $key
* @param mixed $default
* @return mixed * @return mixed
*/ */
public function get($key, $default = null) public function get($key)
{
// --------------------------------------------------
// If the item has already been loaded, return it.
// --------------------------------------------------
if (array_key_exists($key, $this->items))
{ {
return $this->items[$key]; return (($cache = \System\Memcached::instance()->get(Config::get('cache.key').$key)) !== false) ? $cache : null;
}
// --------------------------------------------------
// Attempt to the get the item from cache.
// --------------------------------------------------
$cache = \System\Memcached::instance()->get(\System\Config::get('cache.key').$key);
// --------------------------------------------------
// Verify that the item was retrieved.
// --------------------------------------------------
if ($cache === false)
{
return $default;
}
return $this->items[$key] = $cache;
} }
/** /**
...@@ -63,7 +36,7 @@ class Memcached implements \System\Cache\Driver { ...@@ -63,7 +36,7 @@ class Memcached implements \System\Cache\Driver {
*/ */
public function put($key, $value, $minutes) public function put($key, $value, $minutes)
{ {
\System\Memcached::instance()->set(\System\Config::get('cache.key').$key, $value, 0, $minutes * 60); \System\Memcached::instance()->set(Config::get('cache.key').$key, $value, 0, $minutes * 60);
} }
/** /**
...@@ -74,7 +47,7 @@ class Memcached implements \System\Cache\Driver { ...@@ -74,7 +47,7 @@ class Memcached implements \System\Cache\Driver {
*/ */
public function forget($key) public function forget($key)
{ {
\System\Memcached::instance()->delete(\System\Config::get('cache.key').$key); \System\Memcached::instance()->delete(Config::get('cache.key').$key);
} }
} }
\ No newline at end of file
<?php namespace System\Cache;
class Factory {
/**
* Create a cache driver instance.
*
* @param string $driver
* @return Driver
*/
public static function make($driver)
{
switch ($driver)
{
case 'file':
return new Driver\File;
case 'memcached':
return new Driver\Memcached;
case 'apc':
return new Driver\APC;
default:
throw new \Exception("Cache driver [$driver] is not supported.");
}
}
}
\ No newline at end of file
...@@ -10,7 +10,7 @@ class Config { ...@@ -10,7 +10,7 @@ class Config {
private static $items = array(); private static $items = array();
/** /**
* Determine if a configuration item exists. * Determine if a configuration item or file exists.
* *
* @param string $key * @param string $key
* @return bool * @return bool
...@@ -23,43 +23,36 @@ class Config { ...@@ -23,43 +23,36 @@ class Config {
/** /**
* Get a configuration item. * Get a configuration item.
* *
* Configuration items are retrieved using "dot" notation. So, asking for the
* "application.timezone" configuration item would return the "timezone" option
* from the "application" configuration file.
*
* If the name of a configuration file is passed without specifying an item, the
* entire configuration array will be returned.
*
* @param string $key * @param string $key
* @param string $default * @param string $default
* @return mixed * @return array
*/ */
public static function get($key, $default = null) public static function get($key, $default = null)
{ {
// ----------------------------------------------------- if (strpos($key, '.') === false)
// If a dot is not present, we will just return the
// entire configuration array.
//
// If the configuration file does not exist, the default
// value will be returned.
// -----------------------------------------------------
if(strpos($key, '.') === false)
{ {
static::load($key); static::load($key);
return (array_key_exists($key, static::$items)) ? static::$items[$key] : $default; return Arr::get(static::$items, $key, $default);
} }
list($file, $key) = static::parse($key); list($file, $key) = static::parse($key);
static::load($file); static::load($file);
// -----------------------------------------------------
// If the file doesn't exist, return the default.
// -----------------------------------------------------
if ( ! array_key_exists($file, static::$items)) if ( ! array_key_exists($file, static::$items))
{ {
return $default; return is_callable($default) ? call_user_func($default) : $default;
} }
// ----------------------------------------------------- return Arr::get(static::$items[$file], $key, $default);
// Return the configuration item. If the item doesn't
// exist, the default value will be returned.
// -----------------------------------------------------
return (array_key_exists($key, static::$items[$file])) ? static::$items[$file][$key] : $default;
} }
/** /**
...@@ -81,19 +74,14 @@ class Config { ...@@ -81,19 +74,14 @@ class Config {
/** /**
* Parse a configuration key. * Parse a configuration key.
* *
* The value on the left side of the dot is the configuration file
* name, while the right side of the dot is the item within that file.
*
* @param string $key * @param string $key
* @return array * @return array
*/ */
private static function parse($key) private static function parse($key)
{ {
// -----------------------------------------------------
// The left side of the dot is the file name, while
// the right side of the dot is the item within that
// file being requested.
//
// This syntax allows for the easy retrieval and setting
// of configuration items.
// -----------------------------------------------------
$segments = explode('.', $key); $segments = explode('.', $key);
if (count($segments) < 2) if (count($segments) < 2)
...@@ -105,26 +93,17 @@ class Config { ...@@ -105,26 +93,17 @@ class Config {
} }
/** /**
* Load all of the configuration items. * Load all of the configuration items from a file.
* *
* @param string $file * @param string $file
* @return void * @return void
*/ */
public static function load($file) public static function load($file)
{ {
// ----------------------------------------------------- if ( ! array_key_exists($file, static::$items) and file_exists($path = APP_PATH.'config/'.$file.EXT))
// Bail out if already loaded or doesn't exist.
// -----------------------------------------------------
if (array_key_exists($file, static::$items) or ! file_exists($path = APP_PATH.'config/'.$file.EXT))
{ {
return;
}
// -----------------------------------------------------
// Load the configuration array into the array of items.
// The items array is keyed by filename.
// -----------------------------------------------------
static::$items[$file] = require $path; static::$items[$file] = require $path;
} }
}
} }
\ No newline at end of file
...@@ -2,91 +2,6 @@ ...@@ -2,91 +2,6 @@
class Cookie { class Cookie {
/**
* The cookie name.
*
* @var string
*/
public $name;
/**
* The cookie value.
*
* @var mixed
*/
public $value;
/**
* The number of minutes the cookie should live.
*
* @var int
*/
public $lifetime = 0;
/**
* The path for which the cookie is available.
*
* @var string
*/
public $path = '/';
/**
* The domain for which the cookie is available.
*
* @var string
*/
public $domain = null;
/**
* Indicates if the cookie should only be sent over HTTPS.
*
* @var bool
*/
public $secure = false;
/**
* Create a new Cookie instance.
*
* Note: Cookies can be sent using the Cookie::put method.
* However, the number of parameters that method requires
* is somewhat cumbersome. Instantiating a new Cookie class
* and setting the properties can be a little easier on the eyes.
*
* @param string $name
* @return void
*/
public function __construct($name, $value = null)
{
$this->name = $name;
$this->value = $value;
}
/**
* Create a new Cookie instance.
*
* @param string $name
* @return Cookie
*/
public static function make($name, $value = null)
{
return new static($name, $value);
}
/**
* Send the current cookie instance to the user's machine.
*
* @return bool
*/
public function send()
{
if (is_null($this->name))
{
throw new \Exception("Attempting to send cookie without a name.");
}
return static::put($this->name, $this->value, $this->lifetime, $this->path, $this->domain, $this->secure);
}
/** /**
* Determine if a cookie exists. * Determine if a cookie exists.
* *
......
...@@ -24,38 +24,16 @@ class Crypt { ...@@ -24,38 +24,16 @@ class Crypt {
*/ */
public static function encrypt($value) public static function encrypt($value)
{ {
// ----------------------------------------------------- // Seed the system random number generator if it is being used.
// Determine the input vector source. Different servers if (($random = static::randomizer()) === MCRYPT_RAND)
// and operating systems will have varying options.
// -----------------------------------------------------
if (defined('MCRYPT_DEV_URANDOM'))
{
$random = MCRYPT_DEV_URANDOM;
}
elseif (defined('MCRYPT_DEV_RANDOM'))
{
$random = MCRYPT_DEV_RANDOM;
}
else
{
$random = MCRYPT_RAND;
}
// -----------------------------------------------------
// The system random number generator must be seeded
// to produce adequately random results.
// -----------------------------------------------------
if ($random === MCRYPT_RAND)
{ {
mt_srand(); mt_srand();
} }
$iv = mcrypt_create_iv(static::iv_size(), $random); $iv = mcrypt_create_iv(static::iv_size(), $random);
$value = mcrypt_encrypt(static::$cipher, static::key(), $value, static::$mode, $iv); $value = mcrypt_encrypt(static::$cipher, static::key(), $value, static::$mode, $iv);
// -----------------------------------------------------
// We use base64 encoding to get a nice string value.
// -----------------------------------------------------
return base64_encode($iv.$value); return base64_encode($iv.$value);
} }
...@@ -67,10 +45,6 @@ class Crypt { ...@@ -67,10 +45,6 @@ class Crypt {
*/ */
public static function decrypt($value) public static function decrypt($value)
{ {
// -----------------------------------------------------
// Since all of our encrypted values are base64 encoded,
// we will decode the value here and verify it.
// -----------------------------------------------------
$value = base64_decode($value, true); $value = base64_decode($value, true);
if ( ! $value) if ( ! $value)
...@@ -78,21 +52,38 @@ class Crypt { ...@@ -78,21 +52,38 @@ class Crypt {
throw new \Exception('Decryption error. Input value is not valid base64 data.'); throw new \Exception('Decryption error. Input value is not valid base64 data.');
} }
// -----------------------------------------------------
// Extract the input vector from the value. // Extract the input vector from the value.
// -----------------------------------------------------
$iv = substr($value, 0, static::iv_size()); $iv = substr($value, 0, static::iv_size());
// -----------------------------------------------------
// Remove the input vector from the encrypted value. // Remove the input vector from the encrypted value.
// -----------------------------------------------------
$value = substr($value, static::iv_size()); $value = substr($value, static::iv_size());
return rtrim(mcrypt_decrypt(static::$cipher, static::key(), $value, static::$mode, $iv), "\0"); return rtrim(mcrypt_decrypt(static::$cipher, static::key(), $value, static::$mode, $iv), "\0");
} }
/** /**
* Get the application key. * Get the random number source that should be used for the OS.
*
* @return int
*/
private static function randomizer()
{
if (defined('MCRYPT_DEV_URANDOM'))
{
return MCRYPT_DEV_URANDOM;
}
elseif (defined('MCRYPT_DEV_RANDOM'))
{
return MCRYPT_DEV_RANDOM;
}
else
{
return MCRYPT_RAND;
}
}
/**
* Get the application key from the application configuration file.
* *
* @return string * @return string
*/ */
......
...@@ -7,10 +7,13 @@ class DB { ...@@ -7,10 +7,13 @@ class DB {
* *
* @var array * @var array
*/ */
private static $connections = array(); public static $connections = array();
/** /**
* Get a database connection. * Get a database connection. If no database name is specified, the default
* connection will be returned as defined in the db configuration file.
*
* Note: Database connections are managed as singletons.
* *
* @param string $connection * @param string $connection
* @return PDO * @return PDO
...@@ -22,22 +25,9 @@ class DB { ...@@ -22,22 +25,9 @@ class DB {
$connection = Config::get('db.default'); $connection = Config::get('db.default');
} }
// ---------------------------------------------------
// If we have already established this connection,
// simply return the existing connection.
//
// Don't want to establish the same connection twice!
// ---------------------------------------------------
if ( ! array_key_exists($connection, static::$connections)) if ( ! array_key_exists($connection, static::$connections))
{ {
$config = Config::get('db.connections'); static::$connections[$connection] = DB\Connector::connect($connection);
if ( ! array_key_exists($connection, $config))
{
throw new \Exception("Database connection [$connection] is not defined.");
}
static::$connections[$connection] = DB\Connector::connect((object) $config[$connection]);
} }
return static::$connections[$connection]; return static::$connections[$connection];
...@@ -46,6 +36,13 @@ class DB { ...@@ -46,6 +36,13 @@ class DB {
/** /**
* Execute a SQL query against the connection. * Execute a SQL query against the connection.
* *
* The method returns the following based on query type:
*
* SELECT -> Array of stdClasses
* UPDATE -> Number of rows affected.
* DELETE -> Number of Rows affected.
* ELSE -> Boolean true / false depending on success.
*
* @param string $sql * @param string $sql
* @param array $bindings * @param array $bindings
* @param string $connection * @param string $connection
...@@ -55,17 +52,22 @@ class DB { ...@@ -55,17 +52,22 @@ class DB {
{ {
$query = static::connection($connection)->prepare($sql); $query = static::connection($connection)->prepare($sql);
$result = $query->execute($bindings); $bindings = array_values($bindings);
// --------------------------------------------------- foreach ($bindings as $key => &$binding)
// For SELECT statements, the results will be returned {
// as an array of stdClasses. if (is_null($binding))
// {
// For UPDATE and DELETE statements, the number of $query->bindValue($key + 1, null, \PDO::PARAM_INT);
// rows affected by the query will be returned. }
// else
// For all other statements, return a boolean. {
// --------------------------------------------------- $query->bindParam($key + 1, $binding);
}
}
$result = $query->execute();
if (strpos(strtoupper($sql), 'SELECT') === 0) if (strpos(strtoupper($sql), 'SELECT') === 0)
{ {
return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass'); return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass');
......
<?php namespace System\DB; <?php namespace System\DB;
use System\Config;
class Connector { class Connector {
/** /**
...@@ -17,22 +19,37 @@ class Connector { ...@@ -17,22 +19,37 @@ class Connector {
/** /**
* Establish a PDO database connection. * Establish a PDO database connection.
* *
* @param object $config * @param string $connection
* @return PDO * @return PDO
*/ */
public static function connect($config) public static function connect($connection)
{
$config = static::configuration($connection);
switch ($config->driver)
{ {
// ----------------------------------------------------- case 'sqlite':
// Connect to SQLite. return static::connect_to_sqlite($config);
// -----------------------------------------------------
if ($config->driver == 'sqlite') case 'mysql':
case 'pgsql':
return static::connect_to_server($config);
}
throw new \Exception('Database driver '.$config->driver.' is not supported.');
}
/**
* Establish a PDO connection to a SQLite database.
*
* SQLite database paths can be specified either relative to the application/db
* directory, or as an absolute path to any location on the file system.
*
* @param array $config
* @return PDO
*/
private static function connect_to_sqlite($config)
{ {
// -----------------------------------------------------
// Check the application/db directory first.
//
// If the database doesn't exist there, maybe the full
// path was specified as the database name?
// -----------------------------------------------------
if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite')) if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite'))
{ {
return new \PDO('sqlite:'.$path, null, null, static::$options); return new \PDO('sqlite:'.$path, null, null, static::$options);
...@@ -46,14 +63,15 @@ class Connector { ...@@ -46,14 +63,15 @@ class Connector {
throw new \Exception("SQLite database [".$config->database."] could not be found."); throw new \Exception("SQLite database [".$config->database."] could not be found.");
} }
} }
// -----------------------------------------------------
// Connect to MySQL or Postgres. /**
// ----------------------------------------------------- * Connect to a MySQL or PostgreSQL database server.
elseif ($config->driver == 'mysql' or $config->driver == 'pgsql') *
* @param array $config
* @return PDO
*/
private static function connect_to_server($config)
{ {
// -----------------------------------------------------
// Build the PDO connection DSN.
// -----------------------------------------------------
$dsn = $config->driver.':host='.$config->host.';dbname='.$config->database; $dsn = $config->driver.':host='.$config->host.';dbname='.$config->database;
if (isset($config->port)) if (isset($config->port))
...@@ -63,9 +81,6 @@ class Connector { ...@@ -63,9 +81,6 @@ class Connector {
$connection = new \PDO($dsn, $config->username, $config->password, static::$options); $connection = new \PDO($dsn, $config->username, $config->password, static::$options);
// -----------------------------------------------------
// Set the appropriate character set for the datbase.
// -----------------------------------------------------
if (isset($config->charset)) if (isset($config->charset))
{ {
$connection->prepare("SET NAMES '".$config->charset."'")->execute(); $connection->prepare("SET NAMES '".$config->charset."'")->execute();
...@@ -74,7 +89,22 @@ class Connector { ...@@ -74,7 +89,22 @@ class Connector {
return $connection; return $connection;
} }
throw new \Exception('Database driver '.$config->driver.' is not supported.'); /**
* Get the configuration options for a database connection.
*
* @param string $connection
* @return object
*/
private static function configuration($connection)
{
$config = Config::get('db.connections');
if ( ! array_key_exists($connection, $config))
{
throw new \Exception("Database connection [$connection] is not defined.");
}
return (object) $config[$connection];
} }
} }
\ No newline at end of file
This diff is collapsed.
...@@ -5,21 +5,15 @@ use System\DB\Eloquent; ...@@ -5,21 +5,15 @@ use System\DB\Eloquent;
class Hydrator { class Hydrator {
/** /**
* Load the array of hydrated models. * Load the array of hydrated models and their eager relationships.
* *
* @param object $eloquent * @param object $eloquent
* @return array * @return array
*/ */
public static function hydrate($eloquent) public static function hydrate($eloquent)
{ {
// -----------------------------------------------------
// Load the base / parent models from the query results.
// -----------------------------------------------------
$results = static::base(get_class($eloquent), $eloquent->query->get()); $results = static::base(get_class($eloquent), $eloquent->query->get());
// -----------------------------------------------------
// Load all of the eager relationships.
// -----------------------------------------------------
if (count($results) > 0) if (count($results) > 0)
{ {
foreach ($eloquent->includes as $include) foreach ($eloquent->includes as $include)
...@@ -39,6 +33,9 @@ class Hydrator { ...@@ -39,6 +33,9 @@ class Hydrator {
/** /**
* Hydrate the base models for a query. * Hydrate the base models for a query.
* *
* The resulting model array is keyed by the primary keys of the models.
* This allows the models to easily be matched to their children.
*
* @param string $class * @param string $class
* @param array $results * @param array $results
* @return array * @return array
...@@ -52,13 +49,9 @@ class Hydrator { ...@@ -52,13 +49,9 @@ class Hydrator {
$model = new $class; $model = new $class;
$model->attributes = (array) $result; $model->attributes = (array) $result;
$model->exists = true; $model->exists = true;
// -----------------------------------------------------
// The results are keyed by the ID on the record. This
// will allow us to conveniently match them to child
// models during eager loading.
// -----------------------------------------------------
$models[$model->id] = $model; $models[$model->id] = $model;
} }
...@@ -75,39 +68,28 @@ class Hydrator { ...@@ -75,39 +68,28 @@ class Hydrator {
*/ */
private static function eagerly($eloquent, &$parents, $include) private static function eagerly($eloquent, &$parents, $include)
{ {
// -----------------------------------------------------
// Get the relationship Eloquent model. // Get the relationship Eloquent model.
// //
// We temporarily spoof the belongs_to key to allow the // We temporarily spoof the belongs_to key to allow the query to be fetched without
// query to be fetched without any problems, since the // any problems, since the belongs_to method actually gets the attribute.
// belongs_to method actually gets the attribute.
// -----------------------------------------------------
$eloquent->attributes[$spoof = $include.'_id'] = 0; $eloquent->attributes[$spoof = $include.'_id'] = 0;
$relationship = $eloquent->$include(); $relationship = $eloquent->$include();
unset($eloquent->attributes[$spoof]); unset($eloquent->attributes[$spoof]);
// ----------------------------------------------------- // Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon.
// Reset the WHERE clause and bindings on the query.
// We'll add our own WHERE clause soon.
// -----------------------------------------------------
$relationship->query->where = 'WHERE 1 = 1'; $relationship->query->where = 'WHERE 1 = 1';
$relationship->query->bindings = array(); $relationship->query->bindings = array();
// ----------------------------------------------------- // Initialize the relationship attribute on the parents. As expected, "many" relationships
// Initialize the relationship attribute on the parents. // are initialized to an array and "one" relationships are initialized to null.
// As expected, "many" relationships are initialized to
// an array and "one" relationships to null.
// -----------------------------------------------------
foreach ($parents as &$parent) foreach ($parents as &$parent)
{ {
$parent->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null; $parent->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null;
} }
// -----------------------------------------------------
// Eagerly load the relationships. Phew, almost there!
// -----------------------------------------------------
if ($eloquent->relating == 'has_one') if ($eloquent->relating == 'has_one')
{ {
static::eagerly_load_one($relationship, $parents, $eloquent->relating_key, $include); static::eagerly_load_one($relationship, $parents, $eloquent->relating_key, $include);
...@@ -138,14 +120,6 @@ class Hydrator { ...@@ -138,14 +120,6 @@ class Hydrator {
*/ */
private static function eagerly_load_one($relationship, &$parents, $relating_key, $include) private static function eagerly_load_one($relationship, &$parents, $relating_key, $include)
{ {
// -----------------------------------------------------
// Get the all of the related models by the parent IDs.
//
// Remember, the parent results are keyed by ID. So, we
// can simply pass the keys of the array into the query.
//
// After getting the models, we'll match by ID.
// -----------------------------------------------------
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child) foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{ {
$parents[$child->$relating_key]->ignore[$include] = $child; $parents[$child->$relating_key]->ignore[$include] = $child;
...@@ -181,11 +155,6 @@ class Hydrator { ...@@ -181,11 +155,6 @@ class Hydrator {
*/ */
private static function eagerly_load_belonging($relationship, &$parents, $relating_key, $include) private static function eagerly_load_belonging($relationship, &$parents, $relating_key, $include)
{ {
// -----------------------------------------------------
// Gather the keys from the parent models. Since the
// foreign key is on the parent model for this type of
// relationship, we have to gather them individually.
// -----------------------------------------------------
$keys = array(); $keys = array();
foreach ($parents as &$parent) foreach ($parents as &$parent)
...@@ -193,14 +162,8 @@ class Hydrator { ...@@ -193,14 +162,8 @@ class Hydrator {
$keys[] = $parent->$relating_key; $keys[] = $parent->$relating_key;
} }
// -----------------------------------------------------
// Get the related models.
// -----------------------------------------------------
$children = $relationship->where_in('id', array_unique($keys))->get(); $children = $relationship->where_in('id', array_unique($keys))->get();
// -----------------------------------------------------
// Match the child models with their parent by ID.
// -----------------------------------------------------
foreach ($parents as &$parent) foreach ($parents as &$parent)
{ {
if (array_key_exists($parent->$relating_key, $children)) if (array_key_exists($parent->$relating_key, $children))
...@@ -225,37 +188,24 @@ class Hydrator { ...@@ -225,37 +188,24 @@ class Hydrator {
{ {
$relationship->query->select = null; $relationship->query->select = null;
// ----------------------------------------------------- $relationship->query->where_in($relating_table.'.'.$relating_key, array_keys($parents));
// Retrieve the raw results as stdClasses.
// // The foreign key is added to the select to allow us to easily match the models back to their parents.
// We also add the foreign key to the select which will allow us $children = $relationship->query->get(array(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key));
// to match the models back to their parents.
// -----------------------------------------------------
$children = $relationship->query
->where_in($relating_table.'.'.$relating_key, array_keys($parents))
->get(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key);
$class = get_class($relationship); $class = get_class($relationship);
// -----------------------------------------------------
// Create the related models.
// -----------------------------------------------------
foreach ($children as $child) foreach ($children as $child)
{ {
$related = new $class; $related = new $class;
$related->attributes = (array) $child; $related->attributes = (array) $child;
$related->exists = true; $related->exists = true;
// ----------------------------------------------------- // Remove the foreign key since it was added to the query to help match to the children.
// Remove the foreign key from the attributes since it
// was added to the query to help us match the models.
// -----------------------------------------------------
unset($related->attributes[$relating_key]); unset($related->attributes[$relating_key]);
// -----------------------------------------------------
// Match the child model its parent by ID.
// -----------------------------------------------------
$parents[$child->$relating_key]->ignore[$include][$child->id] = $related; $parents[$child->$relating_key]->ignore[$include][$child->id] = $related;
} }
} }
......
...@@ -115,16 +115,48 @@ class Query { ...@@ -115,16 +115,48 @@ class Query {
/** /**
* Add columns to the SELECT clause. * Add columns to the SELECT clause.
* *
* @param array $columns
* @return Query * @return Query
*/ */
public function select() public function select($columns = array('*'))
{ {
$this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; $this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';
$this->select .= implode(', ', array_map(array($this, 'wrap'), func_get_args()));
$wrapped = array();
foreach ($columns as $column)
{
// If the column name is being aliased, we will need to wrap the column
// name and its alias in keyword identifiers.
if (strpos(strtolower($column), ' as ') !== false)
{
$segments = explode(' ', $column);
$wrapped[] = $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]);
}
else
{
$wrapped[] = $this->wrap($column);
}
}
$this->select .= implode(', ', $wrapped);
return $this; return $this;
} }
/**
* Set the FROM clause.
*
* @param string $from
* @return Query
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/** /**
* Add a join to the query. * Add a join to the query.
* *
...@@ -358,33 +390,37 @@ class Query { ...@@ -358,33 +390,37 @@ class Query {
* Find a record by the primary key. * Find a record by the primary key.
* *
* @param int $id * @param int $id
* @param array $columns
* @return object * @return object
*/ */
public function find($id) public function find($id, $columns = array('*'))
{ {
return $this->where('id', '=', $id)->first(); return $this->where('id', '=', $id)->first($columns);
} }
/** /**
* Execute the query as a SELECT statement and return the first result. * Execute the query as a SELECT statement and return the first result.
* *
* @param array $columns
* @return object * @return object
*/ */
public function first() public function first($columns = array('*'))
{ {
return (count($results = call_user_func_array(array($this->take(1), 'get'), func_get_args())) > 0) ? $results[0] : null;
return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null;
} }
/** /**
* Execute the query as a SELECT statement. * Execute the query as a SELECT statement.
* *
* @param array $columns
* @return array * @return array
*/ */
public function get() public function get($columns = array('*'))
{ {
if (is_null($this->select)) if (is_null($this->select))
{ {
call_user_func_array(array($this, 'select'), (count(func_get_args()) > 0) ? func_get_args() : array('*')); $this->select($columns);
} }
return DB::query(Query\Compiler::select($this), $this->bindings, $this->connection); return DB::query(Query\Compiler::select($this), $this->bindings, $this->connection);
...@@ -400,6 +436,7 @@ class Query { ...@@ -400,6 +436,7 @@ class Query {
private function aggregate($aggregator, $column) private function aggregate($aggregator, $column)
{ {
$this->select = 'SELECT '.$aggregator.'('.$this->wrap($column).') AS '.$this->wrap('aggregate'); $this->select = 'SELECT '.$aggregator.'('.$this->wrap($column).') AS '.$this->wrap('aggregate');
return $this->first()->aggregate; return $this->first()->aggregate;
} }
...@@ -424,10 +461,8 @@ class Query { ...@@ -424,10 +461,8 @@ class Query {
{ {
$sql = Query\Compiler::insert($this, $values); $sql = Query\Compiler::insert($this, $values);
// --------------------------------------------------------- // Use the RETURNING clause on Postgres instead of the PDO lastInsertID method.
// Use the RETURNING clause on Postgres instead of PDO. // The PDO method is a little cumbersome using Postgres.
// The Postgres PDO ID method is slightly cumbersome.
// ---------------------------------------------------------
if (DB::driver($this->connection) == 'pgsql') if (DB::driver($this->connection) == 'pgsql')
{ {
$query = DB::connection($this->connection)->prepare($sql.' RETURNING '.$this->wrap('id')); $query = DB::connection($this->connection)->prepare($sql.' RETURNING '.$this->wrap('id'));
...@@ -437,9 +472,6 @@ class Query { ...@@ -437,9 +472,6 @@ class Query {
return $query->fetch(\PDO::FETCH_CLASS, 'stdClass')->id; return $query->fetch(\PDO::FETCH_CLASS, 'stdClass')->id;
} }
// ---------------------------------------------------------
// Use the PDO ID method for MySQL and SQLite.
// ---------------------------------------------------------
DB::query($sql, array_values($values), $this->connection); DB::query($sql, array_values($values), $this->connection);
return DB::connection($this->connection)->lastInsertId(); return DB::connection($this->connection)->lastInsertId();
...@@ -481,6 +513,7 @@ class Query { ...@@ -481,6 +513,7 @@ class Query {
public function wrap($value) public function wrap($value)
{ {
$wrap = (DB::driver($this->connection) == 'mysql') ? '`' : '"'; $wrap = (DB::driver($this->connection) == 'mysql') ? '`' : '"';
return implode('.', array_map(function($segment) use ($wrap) {return ($segment != '*') ? $wrap.$segment.$wrap : $segment;}, explode('.', $value))); return implode('.', array_map(function($segment) use ($wrap) {return ($segment != '*') ? $wrap.$segment.$wrap : $segment;}, explode('.', $value)));
} }
...@@ -500,20 +533,11 @@ class Query { ...@@ -500,20 +533,11 @@ class Query {
*/ */
public function __call($method, $parameters) public function __call($method, $parameters)
{ {
// ---------------------------------------------------------
// Dynamic methods allows the building of very expressive
// queries. All dynamic methods start with "where_".
//
// Ex: DB::table('users')->where_email($email)->first();
// ---------------------------------------------------------
if (strpos($method, 'where_') === 0) if (strpos($method, 'where_') === 0)
{ {
return Query\Dynamic::build($method, $parameters, $this); return Query\Dynamic::build($method, $parameters, $this);
} }
// ---------------------------------------------------------
// Handle any of the aggregate functions.
// ---------------------------------------------------------
if (in_array($method, array('count', 'min', 'max', 'avg', 'sum'))) if (in_array($method, array('count', 'min', 'max', 'avg', 'sum')))
{ {
return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]); return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]);
......
...@@ -14,32 +14,21 @@ class Dynamic { ...@@ -14,32 +14,21 @@ class Dynamic {
*/ */
public static function build($method, $parameters, $query) public static function build($method, $parameters, $query)
{ {
// ---------------------------------------------------------
// Strip the "where_" off of the method. // Strip the "where_" off of the method.
// ---------------------------------------------------------
$finder = substr($method, 6); $finder = substr($method, 6);
// ---------------------------------------------------------
// Split the column names from the connectors. // Split the column names from the connectors.
// ---------------------------------------------------------
$segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE); $segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
// --------------------------------------------------------- // The connector variable will determine which connector will be used for the condition.
// The connector variable will determine which connector // We'll change it as we come across new connectors in the dynamic method string.
// will be used for the condition. We'll change it as we
// come across new connectors in the dynamic method string.
// //
// The index variable helps us get the correct parameter // The index variable helps us get the correct parameter value for the where condition.
// value for the where condition. We increment it each time // We increment it each time we add a condition.
// we add a condition.
// ---------------------------------------------------------
$connector = 'AND'; $connector = 'AND';
$index = 0; $index = 0;
// ---------------------------------------------------------
// Iterate through each segment and add the conditions.
// ---------------------------------------------------------
foreach ($segments as $segment) foreach ($segments as $segment)
{ {
if ($segment != '_and_' and $segment != '_or_') if ($segment != '_and_' and $segment != '_or_')
......
...@@ -31,48 +31,54 @@ class Error { ...@@ -31,48 +31,54 @@ class Error {
*/ */
public static function handle($e) public static function handle($e)
{ {
// -----------------------------------------------------
// Clean the output buffer. We don't want any rendered
// views or text to be sent to the browser.
// -----------------------------------------------------
if (ob_get_level() > 0) if (ob_get_level() > 0)
{ {
ob_clean(); ob_clean();
} }
// -----------------------------------------------------
// Get the error severity in human readable format.
// -----------------------------------------------------
$severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode();
// ----------------------------------------------------- $file = static::file($e);
// Get the error file. Views require special handling
// since view errors occur within eval'd code.
// -----------------------------------------------------
if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false)
{
$file = APP_PATH.'views/'.View::$last.EXT;
}
else
{
$file = $e->getFile();
}
$message = rtrim($e->getMessage(), '.'); $message = rtrim($e->getMessage(), '.');
if (Config::get('error.log')) if (Config::get('error.log'))
{ {
Log::error($message.' in '.$e->getFile().' on line '.$e->getLine()); call_user_func(Config::get('error.logger'), $severity, $message.' in '.$e->getFile().' on line '.$e->getLine());
}
static::show($e, $severity, $message, $file);
exit(1);
}
/**
* Get the path to the file in which an exception occured.
*
* @param Exception $e
* @return string
*/
private static function file($e)
{
if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false)
{
return APP_PATH.'views/'.View::$last.EXT;
} }
// ----------------------------------------------------- return $e->getFile();
// Detailed error view contains the file name and stack }
// trace of the error. It is not wise to have details
// enabled in a production environment. /**
// * Show the error view.
// The generic error view (error/500) only has a simple, *
// generic error message suitable for production. * @param Exception $e
// ----------------------------------------------------- * @param string $severity
* @param string $message
* @param string $file
* @return void
*/
private static function show($e, $severity, $message, $file)
{
if (Config::get('error.detail')) if (Config::get('error.detail'))
{ {
$view = View::make('exception') $view = View::make('exception')
...@@ -89,17 +95,15 @@ class Error { ...@@ -89,17 +95,15 @@ class Error {
{ {
Response::make(View::make('error/500'), 500)->send(); Response::make(View::make('error/500'), 500)->send();
} }
exit(1);
} }
/** /**
* Get the file context of an exception. * Get the code surrounding a given line in a file.
* *
* @param string $path * @param string $path
* @param int $line * @param int $line
* @param int $padding * @param int $padding
* @return array * @return string
*/ */
private static function context($path, $line, $padding = 5) private static function context($path, $line, $padding = 5)
{ {
...@@ -109,17 +113,15 @@ class Error { ...@@ -109,17 +113,15 @@ class Error {
array_unshift($file, ''); array_unshift($file, '');
// ----------------------------------------------------- if (($start = $line - $padding) < 0)
// Calculate the starting position of the file context. {
// ----------------------------------------------------- $start = 0;
$start = $line - $padding; }
$start = ($start < 0) ? 0 : $start;
if (($length = ($line - $start) + $padding + 1) < 0)
// ----------------------------------------------------- {
// Calculate the context length. $length = 0;
// ----------------------------------------------------- }
$length = ($line - $start) + $padding + 1;
$length = (($start + $length) > count($file) - 1) ? null : $length;
return array_slice($file, $start, $length, true); return array_slice($file, $start, $length, true);
} }
......
...@@ -125,11 +125,6 @@ class File { ...@@ -125,11 +125,6 @@ class File {
*/ */
public static function upload($key, $path) public static function upload($key, $path)
{ {
if ( ! array_key_exists($key, $_FILES)) return array_key_exists($key, $_FILES) ? move_uploaded_file($_FILES[$key]['tmp_name'], $path) : false;
{
return false;
}
return move_uploaded_file($_FILES[$key]['tmp_name'], $path);
} }
} }
\ No newline at end of file
This diff is collapsed.
...@@ -3,50 +3,42 @@ ...@@ -3,50 +3,42 @@
class Hash { class Hash {
/** /**
* The salty, hashed value. * Hash a string using PHPass.
* *
* @var string * PHPass provides reliable bcrypt hashing, and is used by many popular PHP
*/ * applications such as Wordpress and Joomla.
public $value;
/**
* The salt used during hashing.
* *
* @var string * @access public
* @param string $value
* @return string
*/ */
public $salt; public static function make($value)
{
return static::hasher()->HashPassword($value);
}
/** /**
* Create a new hash instance. * Determine if an unhashed value matches a given hash.
* *
* @param string $value * @param string $value
* @param string $salt * @param string $hash
* @return void * @return bool
*/ */
public function __construct($value, $salt = null) public static function check($value, $hash)
{ {
// ------------------------------------------------------- return static::hasher()->CheckPassword($value, $hash);
// If no salt is given, we'll create a random salt to
// use when hashing the password.
//
// Otherwise, we will use the given salt.
// -------------------------------------------------------
$this->salt = (is_null($salt)) ? Str::random(16) : $salt;
$this->value = sha1($value.$this->salt);
} }
/** /**
* Factory for creating hash instances. * Create a new PHPass instance.
* *
* @access public * @return PasswordHash
* @param string $value
* @param string $salt
* @return Hash
*/ */
public static function make($value, $salt = null) private static function hasher()
{ {
return new self($value, $salt); require_once SYS_PATH.'vendor/phpass'.EXT;
return new \PasswordHash(10, false);
} }
} }
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -2,46 +2,31 @@ ...@@ -2,46 +2,31 @@
/** /**
* This function is registered on the auto-loader stack by the front controller. * This function is registered on the auto-loader stack by the front controller.
*
* All namespace slashes will be replaced with directory slashes since all Laravel
* system classes are organized using a namespace to directory convention.
*/ */
return function($class) { return function($class) {
// ----------------------------------------------------------
// Replace namespace slashes with directory slashes.
// ----------------------------------------------------------
$file = strtolower(str_replace('\\', '/', $class)); $file = strtolower(str_replace('\\', '/', $class));
// ----------------------------------------------------------
// Should the class be aliased?
// ----------------------------------------------------------
if (array_key_exists($class, $aliases = System\Config::get('aliases'))) if (array_key_exists($class, $aliases = System\Config::get('aliases')))
{ {
return class_alias($aliases[$class], $class); return class_alias($aliases[$class], $class);
} }
// ----------------------------------------------------------
// Is the class a Laravel framework class?
// ----------------------------------------------------------
if (file_exists($path = BASE_PATH.$file.EXT)) if (file_exists($path = BASE_PATH.$file.EXT))
{ {
require $path; require $path;
} }
// ----------------------------------------------------------
// Is the class in the application/models directory?
// ----------------------------------------------------------
elseif (file_exists($path = APP_PATH.'models/'.$file.EXT)) elseif (file_exists($path = APP_PATH.'models/'.$file.EXT))
{ {
require $path; require $path;
} }
// ----------------------------------------------------------
// Is the class in the application/libraries directory?
// ----------------------------------------------------------
elseif (file_exists($path = APP_PATH.'libraries/'.$file.EXT)) elseif (file_exists($path = APP_PATH.'libraries/'.$file.EXT))
{ {
require $path; require $path;
} }
// ----------------------------------------------------------
// Is the class anywhere in the application directory?
// ----------------------------------------------------------
elseif (file_exists($path = APP_PATH.$file.EXT)) elseif (file_exists($path = APP_PATH.$file.EXT))
{ {
require $path; require $path;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -27,6 +27,7 @@ class DB implements \System\Session\Driver { ...@@ -27,6 +27,7 @@ class DB implements \System\Session\Driver {
public function save($session) public function save($session)
{ {
$this->delete($session['id']); $this->delete($session['id']);
$this->table()->insert(array('id' => $session['id'], 'last_activity' => $session['last_activity'], 'data' => serialize($session['data']))); $this->table()->insert(array('id' => $session['id'], 'last_activity' => $session['last_activity'], 'data' => serialize($session['data'])));
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment