Table of Contents
Release Notes
Lumen 5.2.0
Lumen 5.2.0 upgrades the framework to use the 5.2 family of Laravel components, as well as introduces some significant changes to Lumen’s underlying philosophy and purpose.
Only Stateless APIs
Lumen 5.2 represents a shift on slimming Lumen to focus solely on serving stateless, JSON APIs. As such, sessions and views are no longer included with the framework. If you need access to these features, you should use the full Laravel framework. Upgrading your Lumen application to the full Laravel framework mainly involves copying your routes and classes over into a fresh installation of Laravel. Since Laravel and Lumen share many of the same components, your classes should not require any modification.
Authentication
Because sessions are no longer included with Lumen, authentication must be done statelessly using API tokens or headers. You have complete control over the authentication process in the new AuthServiceProvider
. Please review the authentication documentation for more information.
Testing Helpers
Since sessions are no longer included with Lumen, all of the form interaction testing helpers have been removed. The testing helpers for JSON APIs remain, so be sure to review the testing documentation.
Lumen 5.1.0
Lumen 5.1.0 upgrades the framework to use the 5.1 family of Laravel components. Features such as event broadcasting, middleware parameters, and testing improvements are now available in Lumen. For the full Laravel 5.1 release notes, consult the Laravel documentation.
Lumen 5.0.4
When upgrading to Lumen 5.0.4, you should update your bootstrap/app.php
file’s creation of the Lumen application class to the following:
1
$app
=
new
Laravel\Lumen\Application
(
2
realpath
(
__DIR__
.
'/../'
)
3
);
Note: This is not a required change; however, it should prevent some bugs when using the Artisan CLI and PHP’s built-in web server.
Lumen 5.0
Lumen 5.0 is the initial release of the Lumen framework, and is based on the Laravel 5.x series of PHP components.
Upgrade Guide
Upgrading To 5.2.0 From 5.1
Lumen 5.2 represents a more decided shift towards focusing on stateless APIs. Therefore, sessions have been removed from the framework. If you would like to use these features, you should upgrade your Lumen 5.1 application to Laravel 5.2.
Upgrading your Lumen application to the full Laravel framework mainly involves copying your routes and classes over into a fresh installation of Laravel. Since Laravel and Lumen share many of the same components, your classes should not require any modification.
Updating Dependencies
Update your composer.json
file to point to laravel/lumen-framework 5.2.*
and vlucas/phpdotenv ~2.2
.
Bootstrap
In the bootstrap/app.php
file you need to modify the Dotenv::load(...)
method call to the following:
1
try
{
2
(
new
Dotenv\Dotenv
(
__DIR__
.
'/../'
))
->
load
();
3
}
catch
(
Dotenv\Exception\InvalidPathException
$e
)
{
4
//
5
}
Application
Lumen no longer implements the Illuminate\Contracts\Foundation\Application
contract. Any Application
contract type-hints should be updated to reference the Laravel\Lumen\Application
class directly.
Authentication
Since sessions are no longer support in Lumen, authentication is totally based on stateless authentication via API tokens or headers. You should review the full authentication documentation for more information on how to use the authentication system.
Collections
Eloquent Base Collections
The Eloquent collection instance now returns a base Collection (Illuminate\Support\Collection
) for the following methods: pluck
, keys
, zip
, collapse
, flatten
, flip
.
Key Preservation
The slice
, chunk
, and reverse
methods now preserve keys on the collection. If you do not want these methods to preserve keys, use the values
method on the Collection
instance.
Database
MySQL Dates
Starting with MySQL 5.7, 0000-00-00 00:00:00
is no longer considered a valid date, since strict
mode is enabled by default. All timestamp columns should receive a valid default value when you insert records into your database. You may use the useCurrent
method in your migrations to default the timestamp columns to the current timestamps, or you may make the timestamps nullable
to allow null
values:
1
$table
->
timestamp
(
'foo'
)
->
nullable
();
2
3
$table
->
timestamp
(
'foo'
)
->
useCurrent
();
4
5
$table
->
nullableTimestamps
();
MySQL JSON Column Type
The json
column type now creates actual JSON columns when used by the MySQL driver. If you are not running MySQL 5.7 or above, this column type will not be available to you. Instead, use the text
column type in your migration.
Eloquent
Date Casts
Any attributes that have been added to your $casts
property as date
or datetime
will now be converted to a string when toArray
is called on the model or collection of models. This makes the date casting conversion consistent with dates specified in your $dates
array.
Global Scopes
The global scopes implementation has been re-written to be much easier to use. Your global scopes no longer need a remove
method, so it may be removed from any global scopes you have written.
If we were calling getQuery
on an Eloquent query builder to access the underlying query builder instance, you should now call toBase
.
If you were calling the remove
method directly for any reason, you should change this call to $eloquentBuilder->withoutGlobalScope($scope)
.
New methods withoutGlobalScope
and withoutGlobalScopes
have been added to the Eloquent query builder. Any calls to $model->removeGlobalScopes($builder)
may be changed to simply $builder->withoutGlobalScopes()
.
Primary keys
By default, Eloquent assumes your primary keys are integers and will automatically cast them to integers. For any primary key that is not an integer you should override the $incrementing
property on your Eloquent model to false
:
1
/**
2
* Indicates if the IDs are auto-incrementing.
3
*
4
* @var bool
5
*/
6
public
$incrementing
=
true
;
Exception Handling
Your App\Exceptions\Handler
class’ $dontReport
property should be updated to include at least the following exception types:
1
use
Illuminate\Validation\ValidationException
;
2
use
Illuminate\Auth\Access\AuthorizationException
;
3
use
Illuminate\Database\Eloquent\ModelNotFoundException
;
4
use
Symfony\Component\HttpKernel\Exception\HttpException
;
5
6
/**
7
* A list of the exception types that should not be reported.
8
*
9
* @var array
10
*/
11
protected
$dontReport
=
[
12
AuthorizationException
::
class
,
13
HttpException
::
class
,
14
ModelNotFoundException
::
class
,
15
ValidationException
::
class
,
16
];
IronMQ
The IronMQ queue driver has been moved into its own package and is no longer shipped with the core framework.
http://github.com/LaravelCollective/iron-queue
Storage
If you made use of Laravel’s Flysystem integration, you will need to register the filesystem
binding. Add the following code to your bootstrap/app.php
:
1
$app
->
singleton
(
'filesystem'
,
function
(
$app
)
{
2
return
$app
->
loadComponent
(
3
'filesystems'
,
4
Illuminate\Filesystem\FilesystemServiceProvider
::
class
,
5
'filesystem'
6
);
7
});
Validation
The ValidatesRequests
trait has been merged into the ProvidesConvenienceMethods
trait used by Lumen’s base controller.
If you previously used the ValidatesRequests
trait outside of the BaseController, you may copy it from the 5.1 branch or use the full ProvidesConvenienceMethods
trait.
Testing
The DatabaseMigrations
and DatabaseTransactions
traits have moved from Illuminate\Foundation\Testing\DatabaseMigrations
and Illuminate\Foundation\Testing\DatabaseTransactions
to a new location. Update your tests to import the new namespace:
1
<?
php
2
3
use
Laravel\Lumen\Testing\DatabaseMigrations
;
4
use
Laravel\Lumen\Testing\DatabaseTransactions
;
5
6
class
ExampleTest
extends
TestCase
7
{
8
use
DatabaseMigrations
;
9
}
Installation
Installation
Server Requirements
The Lumen framework has a few system requirements. Of course, all of these requirements are satisfied by the Laravel Homestead virtual machine, so it’s highly recommended that you use Homestead as your local Lumen development environment.
However, if you are not using Homestead, you will need to make sure your server meets the following requirements:
- PHP >= 5.5.9
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
Installing Lumen
Lumen utilizes Composer to manage its dependencies. So, before using Lumen, make sure you have Composer installed on your machine.
Via Lumen Installer
First, download the Lumen installer using Composer:
1
composer
global
require
"laravel/lumen-installer"
Make sure to place the ~/.composer/vendor/bin
directory in your PATH so the lumen
executable can be located by your system.
Once installed, the lumen new
command will create a fresh Lumen installation in the directory you specify. For instance, lumen new blog
will create a directory named blog
containing a fresh Lumen installation with all of Lumen’s dependencies already installed. This method of installation is much faster than installing via Composer:
1
lumen
new
blog
Via Composer Create-Project
You may also install Lumen by issuing the Composer create-project
command in your terminal:
1
composer
create
-
project
--
prefer
-
dist
laravel
/
lumen
blog
Configuration
All of the configuration options for the Lumen framework are stored in the .env
file. Once Lumen is installed, you should also configure your local environment.
Application Key
The next thing you should do after installing Lumen is set your application key to a random string. Typically, this string should be 32 characters long. The key can be set in the .env
environment file. If you have not renamed the .env.example
file to .env
, you should do that now. If the application key is not set, your user encrypted data will not be secure!
Configuration
Introduction
All of the configuration options for the Lumen framework are stored in the .env
file.
Accessing Configuration Values
You may easily access your configuration values using the global config
helper function from anywhere in your application. The configuration values may be accessed using “dot” syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist:
1
$value
=
config
(
'app.timezone'
);
To set configuration values at runtime, pass an array to the config
helper:
1
config
([
'app.timezone'
=>
'America/Chicago'
]);
Environment Configuration
It is often helpful to have different configuration values based on the environment the application is running in. For example, you may wish to use a different cache driver locally than you do on your production server. It’s easy using environment based configuration.
To make this a cinch, Lumen utilizes the DotEnv PHP library by Vance Lucas. In a fresh Lumen installation, the root directory of your application will contain a .env.example
file. You should rename the .env.example
file to .env
when creating your application.
All of the variables listed in this file will be loaded into the $_ENV
PHP super-global when your application receives a request. The env
function may be used to retrieve the values of your environment variables:
1
$debug
=
env
(
'APP_DEBUG'
,
true
);
The second value passed to the env
function is the “default value”. This value will be used if no environment variable exists for the given key.
Your .env
file should not be committed to your application’s source control, since each developer / server using your application could require a different environment configuration.
If you are developing with a team, you may wish to continue including a .env.example
file with your application. By putting place-holder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application.
Determining The Current Environment
The current application environment is determined via the APP_ENV
variable from your .env
file. You may access this value via the environment
method on the application instance:
1
$environment
=
app
()
->
environment
();
You may also pass arguments to the environment
method to check if the environment matches a given value. If necessary, you may even pass multiple values to the environment
method. If the environment matches any of the given values, the method will return true
:
1
if
(
app
()
->
environment
(
'local'
))
{
2
// The environment is local
3
}
4
5
if
(
app
()
->
environment
(
'local'
,
'staging'
))
{
6
// The environment is either local OR staging...
7
}
HTTP Routing
Basic Routing
You will define all of the routes for your application in the app/Http/routes.php
file. The most basic Lumen routes simply accept a URI and a Closure
:
1
$app
->
get
(
'foo'
,
function
()
{
2
return
'Hello World'
;
3
});
4
5
$app
->
post
(
'foo'
,
function
()
{
6
//
7
});
Available Router Methods
The router allows you to register routes that respond to any HTTP verb:
1
$app
->
get
(
$uri
,
$callback
);
2
$app
->
post
(
$uri
,
$callback
);
3
$app
->
put
(
$uri
,
$callback
);
4
$app
->
patch
(
$uri
,
$callback
);
5
$app
->
delete
(
$uri
,
$callback
);
6
$app
->
options
(
$uri
,
$callback
);
Route Parameters
Required Parameters
Of course, sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user’s ID from the URL. You may do so by defining route parameters:
1
$app
->
get
(
'user/{id}'
,
function
(
$id
)
{
2
return
'User '
.
$id
;
3
});
You may define as many route parameters as required by your route:
1
$app
->
get
(
'posts/{post}/comments/{comment}'
,
function
(
$postId
,
$commentId
)
{
2
//
3
});
Route parameters are always encased within “curly” braces. The parameters will be passed into your route’s Closure
when the route is executed.
Note: Route parameters cannot contain the
-
character. Use an underscore (_
) instead.
Named Routes
Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route using the as
array key when defining the route:
1
$app
->
get
(
'profile'
,
[
'as'
=>
'profile'
,
function
()
{
2
//
3
}]);
You may also specify route names for controller actions:
1
$app
->
get
(
'profile'
,
[
2
'as'
=>
'profile'
,
'uses'
=>
'UserController@showProfile'
3
]);
Generating URLs To Named Routes
Once you have assigned a name to a given route, you may use the route’s name when generating URLs or redirects via the global route
function:
1
// Generating URLs...
2
$url
=
route
(
'profile'
);
3
4
// Generating Redirects...
5
return
redirect
()
->
route
(
'profile'
);
If the named route defines parameters, you may pass the parameters as the second argument to the route
function. The given parameters will automatically be inserted into the URL in their correct positions:
1
$app
->
get
(
'user/{id}/profile'
,
[
'as'
=>
'profile'
,
function
(
$id
)
{
2
//
3
}]);
4
5
$url
=
route
(
'profile'
,
[
'id'
=>
1
]);
Route Groups
Route groups allow you to share route attributes, such as middleware or namespaces, across a large number of routes without needing to define those attributes on each individual route. Shared attributes are specified in an array format as the first parameter to the $app->group
method.
To learn more about route groups, we’ll walk through several common use-cases for the feature.
Middleware
To assign middleware to all routes within a group, you may use the middleware
key in the group attribute array. Middleware will be executed in the order you define this array:
1
$app
->
group
([
'middleware'
=>
'auth'
],
function
()
use
(
$app
)
{
2
$app
->
get
(
'/'
,
function
()
{
3
// Uses Auth Middleware
4
});
5
6
$app
->
get
(
'user/profile'
,
function
()
{
7
// Uses Auth Middleware
8
});
9
});
Namespaces
Another common use-case for route groups is assigning the same PHP namespace to a group of controllers. You may use the namespace
parameter in your group attribute array to specify the namespace for all controllers within the group:
1
$app
->
group
([
'namespace'
=>
'App\Http\Controllers\Admin'
],
function
()
use
(
$app
)
2
{
3
// Controllers Within The "App\Http\Controllers\Admin" Namespace
4
5
$app
->
group
([
'namespace'
=>
'App\Http\Controllers\Admin\User'
],
function
()
u\
6
se
(
$app
)
{
7
// Controllers Within The "App\Http\Controllers\Admin\User" Namespace
8
});
9
});
Remember, by default, the bootstrap/app.php
file includes your routes.php
file within a namespace group, allowing you to register controller routes without specifying the full App\Http\Controllers
namespace prefix. So, we only need to specify the portion of the namespace that comes after the base App\Http\Controllers
namespace.
Route Prefixes
The prefix
group attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin
:
1
$app
->
group
([
'prefix'
=>
'admin'
],
function
()
use
(
$app
)
{
2
$app
->
get
(
'users'
,
function
()
{
3
// Matches The "/admin/users" URL
4
});
5
});
You may also use the prefix
parameter to specify common parameters for your grouped routes:
1
$app
->
group
([
'prefix'
=>
'accounts/{account_id}'
],
function
()
use
(
$app
)
{
2
$app
->
get
(
'detail'
,
function
(
$accountId
)
{
3
// Matches The "/accounts/{account_id}/detail" URL
4
});
5
});
HTTP Middleware
Introduction
HTTP middleware provide a convenient mechanism for filtering HTTP requests entering your application. For example, Lumen includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application.
Of course, additional middleware can be written to perform a variety of tasks besides authentication. A CORS middleware might be responsible for adding the proper headers to all responses leaving your application. A logging middleware might log all incoming requests to your application.
All middleware should be stored in the app/Http/Middleware
directory.
Defining Middleware
To create a new middleware, copy the ExampleMiddleware
that is included with the default Lumen application. In our new middleware, we will only allow access to the route if the supplied age
is greater than 200. Otherwise, we will redirect the users back to the “home” URI.
1
<?
php
2
3
namespace
App\Http\Middleware
;
4
5
use
Closure
;
6
7
class
OldMiddleware
8
{
9
/**
10
* Run the request filter.
11
*
12
* @param \Illuminate\Http\Request $request
13
* @param \Closure $next
14
* @return mixed
15
*/
16
public
function
handle
(
$request
,
Closure
$next
)
17
{
18
if
(
$request
->
input
(
'age'
)
<=
200
)
{
19
return
redirect
(
'home'
);
20
}
21
22
return
$next
(
$request
);
23
}
24
25
}
As you can see, if the given age
is less than or equal to 200
, the middleware will return an HTTP redirect to the client; otherwise, the request will be passed further into the application. To pass the request deeper into the application (allowing the middleware to “pass”), simply call the $next
callback with the $request
.
It’s best to envision middleware as a series of “layers” HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely.
Before / After Middleware
Whether a middleware runs before or after a request depends on the middleware itself. For example, the following middleware would perform some task before the request is handled by the application:
1
<?
php
2
3
namespace
App\Http\Middleware
;
4
5
use
Closure
;
6
7
class
BeforeMiddleware
8
{
9
public
function
handle
(
$request
,
Closure
$next
)
10
{
11
// Perform action
12
13
return
$next
(
$request
);
14
}
15
}
However, this middleware would perform its task after the request is handled by the application:
1
<?
php
2
3
namespace
App\Http\Middleware
;
4
5
use
Closure
;
6
7
class
AfterMiddleware
8
{
9
public
function
handle
(
$request
,
Closure
$next
)
10
{
11
$response
=
$next
(
$request
);
12
13
// Perform action
14
15
return
$response
;
16
}
17
}
Registering Middleware
Global Middleware
If you want a middleware to be run during every HTTP request to your application, simply list the middleware class in the call to the $app->middleware()
method in your bootstrap/app.php
file:
1
$app
->
middleware
([
2
App\Http\Middleware\OldMiddleware
::
class
3
]);
Assigning Middleware To Routes
If you would like to assign middleware to specific routes, you should first assign the middleware a short-hand key in bootstrap/app.php
file’s call to the $app->routeMiddleware()
method:
1
$app
->
routeMiddleware
([
2
'auth'
=>
App\Http\Middleware\Authenticate
::
class
,
3
]);
Once the middleware has been defined in the HTTP kernel, you may use the middleware
key in the route options array:
1
$app
->
get
(
'admin/profile'
,
[
'middleware'
=>
'auth'
,
function
()
{
2
//
3
}]);
Use an array to assign multiple middleware to the route:
1
$app
->
get
(
'/'
,
[
'middleware'
=>
[
'first'
,
'second'
],
function
()
{
2
//
3
}]);
Middleware Parameters
Middleware can also receive additional custom parameters. For example, if your application needs to verify that the authenticated user has a given “role” before performing a given action, you could create a RoleMiddleware
that receives a role name as an additional argument.
Additional middleware parameters will be passed to the middleware after the $next
argument:
1
<?
php
2
3
namespace
App\Http\Middleware
;
4
5
use
Closure
;
6
7
class
RoleMiddleware
8
{
9
/**
10
* Run the request filter.
11
*
12
* @param \Illuminate\Http\Request $request
13
* @param \Closure $next
14
* @param string $role
15
* @return mixed
16
*/
17
public
function
handle
(
$request
,
Closure
$next
,
$role
)
18
{
19
if
(
!
$request
->
user
()
->
hasRole
(
$role
))
{
20
// Redirect...
21
}
22
23
return
$next
(
$request
);
24
}
25
26
}
Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a :
. Multiple parameters should be delimited by commas:
1
$app
->
put
(
'post/{id}'
,
[
'middleware'
=>
'role:editor'
,
function
(
$id
)
{
2
//
3
}]);
Terminable Middleware
Sometimes a middleware may need to do some work after the HTTP response has already been sent to the browser. For example, the “session” middleware included with Lumen writes the session data to storage after the response has been sent to the browser. To accomplish this, define the middleware as “terminable” by adding a terminate
method to the middleware:
1
<?
php
2
3
namespace
Illuminate\Session\Middleware
;
4
5
use
Closure
;
6
7
class
StartSession
8
{
9
public
function
handle
(
$request
,
Closure
$next
)
10
{
11
return
$next
(
$request
);
12
}
13
14
public
function
terminate
(
$request
,
$response
)
15
{
16
// Store the session data...
17
}
18
}
The terminate
method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of global middleware in your bootstrap/app.php
file.
When calling the terminate
method on your middleware, Lumen will resolve a fresh instance of the middleware from the service container. If you would like to use the same middleware instance when the handle
and terminate
methods are called, register the middleware with the container using the container’s singleton
method.
HTTP Controllers
Introduction
Instead of defining all of your request handling logic in a single routes.php
file, you may wish to organize this behavior using Controller classes. Controllers can group related HTTP request handling logic into a class. Controllers are stored in the app/Http/Controllers
directory.
Basic Controllers
Here is an example of a basic controller class. All Lumen controllers should extend the base controller class included with the default Lumen installation:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
App\User
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* Retrieve the user for the given ID.
11
*
12
* @param int $id
13
* @return Response
14
*/
15
public
function
show
(
$id
)
16
{
17
return
User
::
findOrFail
(
$id
);
18
}
19
}
We can route to the controller action like so:
1
$app
->
get
(
'user/{id}'
,
'UserController@show'
);
Now, when a request matches the specified route URI, the show
method on the UserController
class will be executed. Of course, the route parameters will also be passed to the method.
Controllers & Namespaces
It is very important to note that we did not need to specify the full controller namespace when defining the controller route. We only defined the portion of the class name that comes after the App\Http\Controllers
namespace “root”. By default, the bootstrap/app.php
file will load the routes.php
file within a route group containing the root controller namespace.
If you choose to nest or organize your controllers using PHP namespaces deeper into the App\Http\Controllers
directory, simply use the specific class name relative to the App\Http\Controllers
root namespace. So, if your full controller class is App\Http\Controllers\Photos\AdminController
, you would register a route like so:
1
$app
->
get
(
'foo'
,
'Photos\AdminController@method'
);
Naming Controller Routes
Like Closure routes, you may specify names on controller routes:
1
$app
->
get
(
'foo'
,
[
'uses'
=>
'FooController@method'
,
'as'
=>
'name'
]);
You may also use the route
helper to generate a URL to a named controller route:
1
$url
=
route
(
'name'
);
Controller Middleware
Middleware may be assigned to the controller’s routes like so:
1
$app
->
get
(
'profile'
,
[
2
'middleware'
=>
'auth'
,
3
'uses'
=>
'UserController@showProfile'
4
]);
However, it is more convenient to specify middleware within your controller’s constructor. Using the middleware
method from your controller’s constructor, you may easily assign middleware to the controller. You may even restrict the middleware to only certain methods on the controller class:
1
class
UserController
extends
Controller
2
{
3
/**
4
* Instantiate a new UserController instance.
5
*
6
* @return void
7
*/
8
public
function
__construct
()
9
{
10
$this
->
middleware
(
'auth'
);
11
12
$this
->
middleware
(
'log'
,
[
'only'
=>
[
13
'fooAction'
,
14
'barAction'
,
15
]]);
16
17
$this
->
middleware
(
'subscribed'
,
[
'except'
=>
[
18
'fooAction'
,
19
'barAction'
,
20
]]);
21
}
22
}
Dependency Injection & Controllers
Constructor Injection
The Lumen service container is used to resolve all Lumen controllers. As a result, you are able to type-hint any dependencies your controller may need in its constructor. The dependencies will automatically be resolved and injected into the controller instance:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
App\Repositories\UserRepository
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* The user repository instance.
11
*/
12
protected
$users
;
13
14
/**
15
* Create a new controller instance.
16
*
17
* @param UserRepository $users
18
* @return void
19
*/
20
public
function
__construct
(
UserRepository
$users
)
21
{
22
$this
->
users
=
$users
;
23
}
24
}
Method Injection
In addition to constructor injection, you may also type-hint dependencies on your controller’s action methods. For example, let’s type-hint the Illuminate\Http\Request
instance on one of our methods:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Illuminate\Http\Request
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* Store a new user.
11
*
12
* @param Request $request
13
* @return Response
14
*/
15
public
function
store
(
Request
$request
)
16
{
17
$name
=
$request
->
input
(
'name'
);
18
19
//
20
}
21
}
If your controller method is also expecting input from a route parameter, simply list your route arguments after your other dependencies. For example, if your route is defined like so:
1
$app
->
put
(
'user/{id}'
,
'UserController@update'
);
You may still type-hint the Illuminate\Http\Request
and access your route parameter id
by defining your controller method like the following:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Illuminate\Http\Request
;
6
use
Illuminate\Routing\Controller
;
7
8
class
UserController
extends
Controller
9
{
10
/**
11
* Update the specified user.
12
*
13
* @param Request $request
14
* @param string $id
15
* @return Response
16
*/
17
public
function
update
(
Request
$request
,
$id
)
18
{
19
//
20
}
21
}
HTTP Requests
Accessing The Request
To obtain an instance of the current HTTP request via dependency injection, you should type-hint the Illuminate\Http\Request
class on your controller constructor or method. The current request instance will automatically be injected by the service container:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Illuminate\Http\Request
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* Store a new user.
11
*
12
* @param Request $request
13
* @return Response
14
*/
15
public
function
store
(
Request
$request
)
16
{
17
$name
=
$request
->
input
(
'name'
);
18
19
//
20
}
21
}
If your controller method is also expecting input from a route parameter, simply list your route arguments after your other dependencies. For example, if your route is defined like so:
1
$app
->
put
(
'user/{id}'
,
'UserController@update'
);
You may still type-hint the Illuminate\Http\Request
and access your route parameter id
by defining your controller method like the following:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Illuminate\Http\Request
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* Update the specified user.
11
*
12
* @param Request $request
13
* @param string $id
14
* @return Response
15
*/
16
public
function
update
(
Request
$request
,
$id
)
17
{
18
//
19
}
20
}
Basic Request Information
The Illuminate\Http\Request
instance provides a variety of methods for examining the HTTP request for your application and extends the Symfony\Component\HttpFoundation\Request
class. Here are a few more of the useful methods available on this class:
Retrieving The Request URI
The path
method returns the request’s URI. So, if the incoming request is targeted at http://domain.com/foo/bar
, the path
method will return foo/bar
:
1
$uri
=
$request
->
path
();
The is
method allows you to verify that the incoming request URI matches a given pattern. You may use the *
character as a wildcard when utilizing this method:
1
if
(
$request
->
is
(
'admin/*'
))
{
2
//
3
}
To get the full URL, not just the path info, you may use the url
or fullUrl
methods on the request instance:
1
// Without Query String...
2
$url
=
$request
->
url
();
3
4
// With Query String...
5
$url
=
$request
->
fullUrl
();
Retrieving The Request Method
The method
method will return the HTTP verb for the request. You may also use the isMethod
method to verify that the HTTP verb matches a given string:
1
$method
=
$request
->
method
();
2
3
if
(
$request
->
isMethod
(
'post'
))
{
4
//
5
}
PSR-7 Requests
The PSR-7 standard specifies interfaces for HTTP messages, including requests and responses. If you would like to obtain an instance of a PSR-7 request, you will first need to install a few libraries. Laravel uses the Symfony HTTP Message Bridge component to convert typical Laravel requests and responses into PSR-7 compatible implementations:
1
composer
require
symfony
/
psr
-
http
-
message
-
bridge
2
3
composer
require
zendframework
/
zend
-
diactoros
Once you have installed these libraries, you may obtain a PSR-7 request by simply type-hinting the request type on your route or controller:
1
use
Psr\Http\Message\ServerRequestInterface
;
2
3
$app
->
get
(
'/'
,
function
(
ServerRequestInterface
$request
)
{
4
//
5
});
If you return a PSR-7 response instance from a route or controller, it will automatically be converted back to a Laravel response instance and be displayed by the framework.
Retrieving Input
Retrieving An Input Value
Using a few simple methods, you may access all user input from your Illuminate\Http\Request
instance. You do not need to worry about the HTTP verb used for the request, as input is accessed in the same way for all verbs:
1
$name
=
$request
->
input
(
'name'
);
You may pass a default value as the second argument to the input
method. This value will be returned if the requested input value is not present on the request:
1
$name
=
$request
->
input
(
'name'
,
'Sally'
);
When working on forms with array inputs, you may use “dot” notation to access the arrays:
1
$name
=
$request
->
input
(
'products.0.name'
);
2
3
$names
=
$request
->
input
(
'products.*.name'
);
Determining If An Input Value Is Present
To determine if a value is present on the request, you may use the has
method. The has
method returns true
if the value is present and is not an empty string:
1
if
(
$request
->
has
(
'name'
))
{
2
//
3
}
Retrieving All Input Data
You may also retrieve all of the input data as an array
using the all
method:
1
$input
=
$request
->
all
();
Retrieving A Portion Of The Input Data
If you need to retrieve a sub-set of the input data, you may use the only
and except
methods. Both of these methods will accept a single array
or a dynamic list of arguments:
1
$input
=
$request
->
only
([
'username'
,
'password'
]);
2
3
$input
=
$request
->
only
(
'username'
,
'password'
);
4
5
$input
=
$request
->
except
([
'credit_card'
]);
6
7
$input
=
$request
->
except
(
'credit_card'
);
Files
Retrieving Uploaded Files
You may access uploaded files that are included with the Illuminate\Http\Request
instance using the file
method. The object returned by the file
method is an instance of the Symfony\Component\HttpFoundation\File\UploadedFile
class, which extends the PHP SplFileInfo
class and provides a variety of methods for interacting with the file:
1
$file
=
$request
->
file
(
'photo'
);
You may determine if a file is present on the request using the hasFile
method:
1
if
(
$request
->
hasFile
(
'photo'
))
{
2
//
3
}
Validating Successful Uploads
In addition to checking if the file is present, you may verify that there were no problems uploading the file via the isValid
method:
1
if
(
$request
->
file
(
'photo'
)
->
isValid
())
{
2
//
3
}
Moving Uploaded Files
To move the uploaded file to a new location, you should use the move
method. This method will move the file from its temporary upload location (as determined by your PHP configuration) to a more permanent destination of your choosing:
1
$request
->
file
(
'photo'
)
->
move
(
$destinationPath
);
2
3
$request
->
file
(
'photo'
)
->
move
(
$destinationPath
,
$fileName
);
Other File Methods
There are a variety of other methods available on UploadedFile
instances. Check out the API documentation for the class for more information regarding these methods.
HTTP Responses
Basic Responses
Of course, all routes and controllers should return some kind of response to be sent back to the user’s browser. Lumen provides several different ways to return responses. The most basic response is simply returning a string from a route or controller:
1
$app
->
get
(
'/'
,
function
()
{
2
return
'Hello World'
;
3
});
The given string will automatically be converted into an HTTP response by the framework.
Response Objects
However, for most routes and controller actions, you will be returning a full Illuminate\Http\Response
instance. Returning a full Response
instance allows you to customize the response’s HTTP status code and headers. A Response
instance inherits from the Symfony\Component\HttpFoundation\Response
class, providing a variety of methods for building HTTP responses:
1
use
Illuminate\Http\Response
;
2
3
$app
->
get
(
'home'
,
function
()
{
4
return
(
new
Response
(
$content
,
$status
))
5
->
header
(
'Content-Type'
,
$value
);
6
});
For convenience, you may also use the response
helper:
1
$app
->
get
(
'home'
,
function
()
{
2
return
response
(
$content
,
$status
)
3
->
header
(
'Content-Type'
,
$value
);
4
});
Note: For a full list of available
Response
methods, check out its API documentation and the Symfony API documentation.
Attaching Headers To Responses
Keep in mind that most response methods are chainable, allowing for the fluent building of responses. For example, you may use the header
method to add a series of headers to the response before sending it back to the user:
1
return
response
(
$content
)
2
->
header
(
'Content-Type'
,
$type
)
3
->
header
(
'X-Header-One'
,
'Header Value'
)
4
->
header
(
'X-Header-Two'
,
'Header Value'
);
Or, you may use the withHeaders
method to specify an array of headers to be added to the response:
1
return
response
(
$content
)
2
->
withHeaders
([
3
'Content-Type'
=>
$type
,
4
'X-Header-One'
=>
'Header Value'
,
5
'X-Header-Two'
=>
'Header Value'
,
6
]);
Other Response Types
The response
helper may be used to conveniently generate other types of response instances. When the response
helper is called without arguments, an implementation of the Illuminate\Contracts\Routing\ResponseFactory
contract is returned. This contract provides several helpful methods for generating responses.
JSON Responses
The json
method will automatically set the Content-Type
header to application/json
, as well as convert the given array into JSON using the json_encode
PHP function:
1
return
response
()
->
json
([
'name'
=>
'Abigail'
,
'state'
=>
'CA'
]);
If you would like to create a JSONP response, you may use the json
method in addition to setCallback
:
1
return
response
()
2
->
json
([
'name'
=>
'Abigail'
,
'state'
=>
'CA'
])
3
->
setCallback
(
$request
->
input
(
'callback'
));
File Downloads
The download
method may be used to generate a response that forces the user’s browser to download the file at the given path. The download
method accepts a file name as the second argument to the method, which will determine the file name that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method:
1
return
response
()
->
download
(
$pathToFile
);
2
3
return
response
()
->
download
(
$pathToFile
,
$name
,
$headers
);
Note: Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name.
Redirects
Redirect responses are instances of the Illuminate\Http\RedirectResponse
class, and contain the proper headers needed to redirect the user to another URL. There are several ways to generate a RedirectResponse
instance. The simplest method is to use the global redirect
helper method:
1
$app
->
get
(
'dashboard'
,
function
()
{
2
return
redirect
(
'home/dashboard'
);
3
});
Redirecting To Named Routes
When you call the redirect
helper with no parameters, an instance of Laravel\Lumen\Http\Redirector
is returned, allowing you to call any method on the Redirector
instance. For example, to generate a RedirectResponse
to a named route, you may use the route
method:
1
return
redirect
()
->
route
(
'login'
);
If your route has parameters, you may pass them as the second argument to the route
method:
1
// For a route with the following URI: profile/{id}
2
3
return
redirect
()
->
route
(
'profile'
,
[
'id'
=>
1
]);
If you are redirecting to a route with an “ID” parameter that is being populated from an Eloquent model, you may simply pass the model itself. The ID will be extracted automatically:
1
return
redirect
()
->
route
(
'profile'
,
[
$user
]);
Authentication
Introduction
Authentication in Lumen, while using the same underlying libraries as Laravel, is configured quite differently from the full Laravel framework. Since Lumen does not support session state, incoming requests that you wish to authenticate must be authenticated via a stateless mechanism such as API tokens.
Getting Started
Authentication Service Provider
Note: Before using Lumen’s authentication features, you should uncomment the call to register the
AuthServiceProvider
service provider in yourbootstrap/app.php
file.
The AuthServiceProvider
located in your app/Providers
directory contains a single call to Auth::viaRequest
. The viaRequest
method accepts a Closure which will be called when the incoming request needs to be authenticated. Within this Closure, you may resolve your App\User
instance however you wish. If no authenticated user can be found for the request, the Closure should return null
:
1
$this
->
app
[
'auth'
]
->
viaRequest
(
'api'
,
function
(
$request
)
{
2
// Return User or null...
3
});
Again, you may retrieve the authenticated user however you wish. You may use an API token in the request headers or query string, a bearer token on the request, or using any other approach your application requires.
Accessing The Authenticated User
Just like in the full Laravel framework, you may use the Auth::user()
method to retrieve the current user. Alternatively, you may use the $request->user()
method on an Illuminate\Http\Request
instance:
1
use
Illuminate\Http\Request
;
2
3
$app
->
get
(
'/post/{id}'
,
[
'middleware'
=>
'auth'
,
function
(
Request
$request
,
$id\
4
)
{
5
$user
=
Auth
::
user
();
6
7
$user
=
$request
->
user
();
8
9
//
10
}]);
Note: If you would like to use
Auth::user()
to access the currently authenticated user, you should uncomment the$app->withFacades()
method in yourbootstrap/app.php
file.
Of course, any routes you wish to authenticate should be assigned the auth
middleware, so you should uncomment the call to $app->routeMiddleware()
in your bootstrap/app.php
file:
1
$app
->
routeMiddleware
([
2
'auth'
=>
App\Http\Middleware\Authenticate
::
class
,
3
]);
Authorization
Introduction
In addition to providing authentication services out of the box, Lumen also provides a simple way to organize authorization logic and control access to resources. There are a variety of methods and helpers to assist you in organizing your authorization logic.
In general, authorization can be used in Lumen the same way it is used in Laravel. We will cover a few differences here, but you should refer to the full Laravel documentation for additional details.
Differences From Laravel
Defining Abilities
The primary difference when using authorization in Lumen compared to Laravel is in regards to how abilities are defined. In Lumen, you may simply use the Gate
facade in your AuthServiceProvider
to define abilities:
1
Gate
::
define
(
'update-post'
,
function
(
$user
,
$post
)
{
2
return
$user
->
id
===
$post
->
user_id
;
3
});
Defining Policies
Unlike Laravel, Lumen does not have a $policies
array on its AuthServiceProvider
. However, you may still call the policy
method on the Gate
facade from within the provider’s boot
method:
1
Gate
::
policy
(
Post
::
class
,
PostPolicy
::
class
);
Again, to learn more about policies, you should consult the full Laravel documentation.
Checking Abilities
You may “check” abilities just as you would in the full Laravel framework. First, you may use the Gate
facade. If you choose to use the facade, be sure to enable facades in your bootstrap/app.php
file. Remember, we don’t need to pass the User
instance into the allows
method since the currently authenticated user will automatically be passed to your authorization callback:
1
if
(
Gate
::
allows
(
'update-post'
,
$post
))
{
2
//
3
}
4
5
if
(
Gate
::
denies
(
'update-post'
,
$post
))
{
6
abort
(
403
);
7
}
Of course, you may also check if a given User
instance has a given ability:
1
if
(
$request
->
user
()
->
can
(
'update-post'
,
$post
))
{
2
abort
(
403
);
3
}
4
5
if
(
$request
->
user
()
->
cannot
(
'update-post'
,
$post
))
{
6
abort
(
403
);
7
}
Cache
Introduction
Laravel provides a unified API for various caching systems. The cache configuration is located in the .env
file. In this file you may specify which cache driver you would like used by default throughout your application. Laravel supports popular caching backends like Memcached and Redis out of the box.
Differences From Laravel
The Lumen cache drivers utilize the exact same code as the full Laravel cache drivers. Beyond configuration, there are no differences between using the cache in Lumen and using the cache in Laravel; therefore, please consult the full Laravel documentation for usage examples.
Note: Before using the
Cache
facade, be sure you have uncommented the$app->withFacades()
method call in yourbootstrap/app.php
file.
Redis Support
Before using a Redis cache with Lumen, you will need to install the predis/predis (~1.0)
and illuminate/redis (5.2.*)
packages via Composer. Then, you should register the Illuminate\Redis\RedisServiceProvider
in your bootstrap/app.php
file.
If you have not called $app->withEloquent()
in your bootstrap/app.php
file, then you should call $app->configure('database');
in the bootstrap/app.php
file to ensure the Redis database configuration is properly loaded.
Database
Configuration
Lumen makes connecting with databases and running queries extremely simple. Currently Lumen supports four database systems: MySQL, Postgres, SQLite, and SQL Server.
You may use the DB_*
configuration options in your .env
configuration file to configure your database settings, such as the driver, host, username, and password.
Basic Usage
Note: If you would like to use the
DB
facade, you should uncomment the$app->withFacades()
call in yourbootstrap/app.php
file.
For example, without facades enabled, you may access a database connection via the app
helper:
1
$results
=
app
(
'db'
)
->
select
(
"SELECT * FROM users"
);
Or, with facades enabled, you may access the database connection via the DB
facade:
1
$results
=
DB
::
select
(
"SELECT * FROM users"
);
Basic Queries
To learn how to execute basic, raw SQL queries via the database component, you may consult the full Laravel documentation.
Query Builder
Lumen may also utilize the Laravel fluent query builder. To learn more about this feature, consult the full Laravel documentation.
Eloquent ORM
If you would like to use the Eloquent ORM, you should uncomment the $app->withEloquent()
call in your bootstrap/app.php
file.
Of course, you may easily use the full Eloquent ORM with Lumen. To learn how to use Eloquent, check out the full Laravel documentation.
Migrations
For further information on how to create database tables and run migrations, check out the Laravel documentation on the migrations.
Encryption
Configuration
Before using Lumens’s encrypter, you should set the APP_KEY
option of your .env
file to a 32 character, random string. If this value is not properly set, all values encrypted by Lumen will be insecure.
Basic Usage
Encrypting A Value
You may encrypt a value using the Crypt
facade. All encrypted values are encrypted using OpenSSL and the AES-256-CBC
cipher. Furthermore, all encrypted values are signed with a message authentication code (MAC) to detect any modifications to the encrypted string.
For example, we may use the encrypt
method to encrypt a secret and store it on an Eloquent model:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Crypt
;
6
use
App\User
;
7
use
Illuminate\Http\Request
;
8
9
class
UserController
extends
Controller
10
{
11
/**
12
* Store a secret message for the user.
13
*
14
* @param Request $request
15
* @param int $id
16
* @return Response
17
*/
18
public
function
storeSecret
(
Request
$request
,
$id
)
19
{
20
$user
=
User
::
findOrFail
(
$id
);
21
22
$user
->
fill
([
23
'secret'
=>
Crypt
::
encrypt
(
$request
->
secret
)
24
])
->
save
();
25
}
26
}
Decrypting A Value
Of course, you may decrypt values using the decrypt
method on the Crypt
facade. If the value can not be properly decrypted, such as when the MAC is invalid, an Illuminate\Contracts\Encryption\DecryptException
will be thrown:
1
use
Illuminate\Contracts\Encryption\DecryptException
;
2
3
try
{
4
$decrypted
=
Crypt
::
decrypt
(
$encryptedValue
);
5
}
catch
(
DecryptException
$e
)
{
6
//
7
}
Errors & Logging
Introduction
When you start a new Lumen project, error and exception handling is already configured for you. In addition, Lumen is integrated with the Monolog logging library, which provides support for a variety of powerful log handlers.
Configuration
Error Detail
The amount of error detail your application displays through the browser is controlled by the APP_DEBUG
configuration option in your .env
file.
For local development, you should set the APP_DEBUG
environment variable to true
. In your production environment, this value should always be false
.
Custom Monolog Configuration
If you would like to have complete control over how Monolog is configured for your application, you may use the application’s configureMonologUsing
method. You should place a call to this method in your bootstrap/app.php
file:
1
$app
->
configureMonologUsing
(
function
(
$monolog
)
{
2
$monolog
->
pushHandler
(
...
);
3
4
return
$monolog
;
5
});
6
7
return
$app
;
The Exception Handler
All exceptions are handled by the App\Exceptions\Handler
class. This class contains two methods: report
and render
. We’ll examine each of these methods in detail.
The Report Method
The report
method is used to log exceptions or send them to an external service like BugSnag. By default, the report
method simply passes the exception to the base class where the exception is logged. However, you are free to log exceptions however you wish.
For example, if you need to report different types of exceptions in different ways, you may use the PHP instanceof
comparison operator:
1
/**
2
* Report or log an exception.
3
*
4
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
5
*
6
* @param \Exception $e
7
* @return void
8
*/
9
public
function
report
(
Exception
$e
)
10
{
11
if
(
$e
instanceof
CustomException
)
{
12
//
13
}
14
15
return
parent
::
report
(
$e
);
16
}
Ignoring Exceptions By Type
The $dontReport
property of the exception handler contains an array of exception types that will not be logged. By default, exceptions resulting from 404 errors are not written to your log files. You may add other exception types to this array as needed.
The Render Method
The render
method is responsible for converting a given exception into an HTTP response that should be sent back to the browser. By default, the exception is passed to the base class which generates a response for you. However, you are free to check the exception type or return your own custom response:
1
/**
2
* Render an exception into an HTTP response.
3
*
4
* @param \Illuminate\Http\Request $request
5
* @param \Exception $e
6
* @return \Illuminate\Http\Response
7
*/
8
public
function
render
(
$request
,
Exception
$e
)
9
{
10
if
(
$e
instanceof
CustomException
)
{
11
return
response
(
'Custom Message'
);
12
}
13
14
return
parent
::
render
(
$request
,
$e
);
15
}
HTTP Exceptions
Some exceptions describe HTTP error codes from the server. For example, this may be a “page not found” error (404), an “unauthorized error” (401) or even a developer generated 500 error. In order to generate such a response from anywhere in your application, use the following:
1
abort
(
404
);
The abort
method will immediately raise an exception which will be rendered by the exception handler. Optionally, you may provide the response text:
1
abort
(
403
,
'Unauthorized action.'
);
This method may be used at any time during the request’s lifecycle.
Logging
The Lumen logging facilities provide a simple layer on top of the powerful Monolog library. By default, Lumen is configured to create a single log file for your application which is stored in the storage/logs
directory. You may write information to the logs using the Log
facade:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Log
;
6
use
App\User
;
7
use
App\Http\Controllers\Controller
;
8
9
class
UserController
extends
Controller
10
{
11
/**
12
* Show the user for the given ID.
13
*
14
* @param int $id
15
* @return Response
16
*/
17
public
function
show
(
$id
)
18
{
19
Log
::
info
(
'Showing user: '
.
$id
);
20
21
return
User
::
findOrFail
(
$id
);
22
}
23
}
Note: Before using the
Log
facade, be sure you have uncommented the$app->withFacades()
method call in yourbootstrap/app.php
file.
The logger provides the eight logging levels defined in RFC 5424: emergency, alert, critical, error, warning, notice, info and debug.
1
Log
::
emergency
(
$error
);
2
Log
::
alert
(
$error
);
3
Log
::
critical
(
$error
);
4
Log
::
error
(
$error
);
5
Log
::
warning
(
$error
);
6
Log
::
notice
(
$error
);
7
Log
::
info
(
$error
);
8
Log
::
debug
(
$error
);
Contextual Information
An array of contextual data may also be passed to the log methods. This contextual data will be formatted and displayed with the log message:
1
Log
::
info
(
'User failed to login.'
,
[
'id'
=>
$user
->
id
]);
Events
Introduction
Lumen’s events provides a simple observer implementation, allowing you to subscribe and listen for events in your application. Event classes are typically stored in the app/Events
directory, while their listeners are stored in app/Listeners
.
Differences From Laravel
In general, events in Lumen function exactly like they do in the full-stack Laravel framework, so please review the full Laravel documentation. Event broadcasting is even supported in Lumen, which allows you to listen for your server side events in your client-side JavaScript. However, there are a few minor differences which warrant discussion.
Generators
In Lumen, there are no generator commands to generate events and listeners for you, so you should simply copy the ExampleEvent
or ExampleListener
classes to define your own events and listeners. These example classes provide the basic structure of every event and listener.
Registering Events / Listeners
Like the full Laravel framework, the EventServiceProvider
included with your Lumen application provides a convenient place to register all event listeners. The listen
property contains an array of all events (keys) and their listeners (values). Of course, you may add as many events to this array as your application requires:
1
/**
2
* The event listener mappings for the application.
3
*
4
* @var array
5
*/
6
protected
$listen
=
[
7
'App\Events\ExampleEvent'
=>
[
8
'App\Listeners\ExampleListener'
,
9
],
10
];
Firing Events
You may use the event
helper function or Event
facade to fire events throughout your Lumen application. Again, these functions behave exactly like their full Laravel framework equivalent:
1
event
(
new
ExampleEvent
);
2
3
Event
::
fire
(
new
ExampleEvent
);
Queues
Introduction
The Lumen queue service provides a unified API across a variety of different queue back-ends. Queues allow you to defer the processing of a time consuming task, such as performing a task on a remote server, until a later time which drastically speeds up web requests to your application.
Like many other parts of the framework, Lumen’s queued jobs function identically to Laravel’s queued jobs. So, to learn more about queuing jobs in Lumen, please review the full Laravel queue documentation.
Configuration
The queue configuration options are in the .env
file.
If you would like to thoroughly customize the queue configuration, then you must copy the entire vendor/laravel/lumen-framework/config/queue.php
file to the config
directory in the root of your project and adjust the necessary configuration options as needed. If the config
directory does not exist, then you should create it.
Driver Prerequisites
Database
In order to use the database
queue driver, you will need a database table to hold the jobs:
1
Schema
::
create
(
'jobs'
,
function
(
Blueprint
$table
)
{
2
$table
->
bigIncrements
(
'id'
);
3
$table
->
string
(
'queue'
);
4
$table
->
longText
(
'payload'
);
5
$table
->
tinyInteger
(
'attempts'
)
->
unsigned
();
6
$table
->
tinyInteger
(
'reserved'
)
->
unsigned
();
7
$table
->
unsignedInteger
(
'reserved_at'
)
->
nullable
();
8
$table
->
unsignedInteger
(
'available_at'
);
9
$table
->
unsignedInteger
(
'created_at'
);
10
$table
->
index
([
'queue'
,
'reserved'
,
'reserved_at'
]);
11
});
Other Queue Dependencies
The following dependencies are needed for the listed queue drivers:
- Amazon SQS:
aws/aws-sdk-php ~3.0
- Beanstalkd:
pda/pheanstalk ~3.0
- Redis:
predis/predis ~1.0
Differences From Laravel
Like many other parts of the framework, Lumen’s queued jobs function identically to Laravel’s queued jobs. So, to learn more about queuing jobs in Lumen, please review the full Laravel queue documentation.
However, there are a few minor differences that we will discuss now. First, let’s talk about how queued jobs are generated in Lumen.
Generators
Lumen does not include generators for automatically creating new Job classes. Instead, you should copy the ExampleJob
class that is included with the framework. This class provides the basic structure that is shared by every Job class. The base Job
that is used by the ExampleJob
already includes the needed InteractsWithQueue
, Queueable
, and SerializesModels
traits:
1
<?
php
2
3
namespace
App\Jobs
;
4
5
class
ExampleJob
extends
Job
6
{
7
/**
8
* Create a new job instance.
9
*
10
* @return void
11
*/
12
public
function
__construct
()
13
{
14
//
15
}
16
17
/**
18
* Execute the job.
19
*
20
* @return void
21
*/
22
public
function
handle
()
23
{
24
//
25
}
26
}
Dispatching Jobs
Again, you should consult the full Laravel documentation for complete information on dispatching queued jobs; however, just like in the Laravel framework, you may use the dispatch
function to dispatch jobs from anywhere within your Lumen application:
1
dispatch
(
new
ExampleJob
);
Of course, you may also use the Queue
facade. If you choose to use the facade, be sure to uncomment the call to $app->withFacades()
in your bootstrap/app.php
file:
1
Queue
::
push
(
new
ExampleJob
);
Service Container
Introduction
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are “injected” into the class via the constructor or, in some cases, “setter” methods.
Differences From Laravel
Lumen uses the exact same service container as the Laravel framework, so you have access to all of its powerful features. For complete documentation on the container, check out the full Laravel container documentation.
Accessing The Container
The Laravel\Lumen\Application
instance is an extension of Illuminate\Container\Container
, so may be treated as the service container for your application.
Typically, you will register bindings into the container within your service providers. Of course, you may use the bind
, singleton
, instance
, and other container methods provided by the container. Remember, all of these methods are documented in the full Laravel container documentation.
Resolving Instances
To resolve things out of the container, you may either type-hint the dependency you need on a class that is already automatically resolved by the container, such as a route Closure, controller constructor, controller method, middleware, event listener, or queued job. Or, you may use the app
function from anywhere in your application:
1
$instance
=
app
(
Something
::
class
);
Service Providers
Introduction
Service providers are the central place of all Lumen application bootstrapping. Your own application, as well as all of Lumen’s core services are bootstrapped via service providers.
But, what do we mean by “bootstrapped”? In general, we mean registering things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application.
If you open the bootstrap/app.php
file included with Lumen, you will see a call to $app->register()
. You may add additional calls to this method to register as many service providers as your application requires.
Writing Service Providers
All service providers extend the Illuminate\Support\ServiceProvider
class. This abstract class requires that you define at least one method on your provider: register
. Within the register
method, you should only bind things into the service container. You should never attempt to register any event listeners, routes, or any other piece of functionality within the register
method.
The Register Method
As mentioned previously, within the register
method, you should only bind things into the service container. You should never attempt to register any event listeners, routes, or any other piece of functionality within the register
method. Otherwise, you may accidentally use a service that is provided by a service provider which has not loaded yet.
Now, let’s take a look at a basic service provider:
1
<?
php
2
3
namespace
App\Providers
;
4
5
use
Riak\Connection
;
6
use
Illuminate\Support\ServiceProvider
;
7
8
class
RiakServiceProvider
extends
ServiceProvider
9
{
10
/**
11
* Register bindings in the container.
12
*
13
* @return void
14
*/
15
public
function
register
()
16
{
17
$this
->
app
->
singleton
(
Connection
::
class
,
function
(
$app
)
{
18
return
new
Connection
(
config
(
'riak'
));
19
});
20
}
21
}
This service provider only defines a register
method, and uses that method to define an implementation of Riak\Connection
in the service container. If you don’t understand how the service container works, check out its documentation.
The Boot Method
So, what if we need to register a view composer within our service provider? This should be done within the boot
method. This method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework:
1
<?
php
2
3
namespace
App\Providers
;
4
5
use
Queue
;
6
use
Illuminate\Support\ServiceProvider
;
7
8
class
AppServiceProvider
extends
ServiceProvider
9
{
10
// Other Service Provider Properties...
11
12
/**
13
* Bootstrap any application services.
14
*
15
* @return void
16
*/
17
public
function
boot
()
18
{
19
Queue
::
failing
(
function
(
$event
)
{
20
21
});
22
}
23
}
Registering Providers
All service providers are registered in the bootstrap/app.php
file. This file contains a call to the $app->register()
method. You may add as many calls to the register method as needed to register all of your providers.
Testing
Introduction
Lumen is built with testing in mind. In fact, support for testing with PHPUnit is included out of the box, and a phpunit.xml
file is already setup for your application. The framework also ships with convenient helper methods allowing you to expressively test your application’s JSON responses.
An ExampleTest.php
file is provided in the tests
directory. After installing a new Lumen application, simply run phpunit
on the command line to run your tests.
Test Environment
Lumen automatically configures the cache to the array
driver while testing, meaning no cache data will be persisted while testing.
You are free to create other testing environment configurations as necessary. The testing
environment variables may be configured in the phpunit.xml
file.
Defining & Running Tests
To create a test case, simply create a new test file in the tests
directory. The test class should extend TestCase
. You may then define test methods as you normally would using PHPUnit. To run your tests, simply execute the phpunit
command from your terminal:
1
<?
php
2
3
class
FooTest
extends
TestCase
4
{
5
public
function
testSomethingIsTrue
()
6
{
7
$this
->
assertTrue
(
true
);
8
}
9
}
Note: If you define your own
setUp
method within a test class, be sure to callparent::setUp
.
Application Testing
Lumen provides a very fluent API for making HTTP requests to your application and examining the output.
Testing JSON APIs
Lumen also provides several helpers for testing JSON APIs and their responses. For example, the get
, post
, put
, patch
, and delete
methods may be used to issue requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let’s write a test to make a POST
request to /user
and assert that a given array was returned in JSON format:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
/**
6
* A basic functional test example.
7
*
8
* @return void
9
*/
10
public
function
testBasicExample
()
11
{
12
$this
->
json
(
'POST'
,
'/user'
,
[
'name'
=>
'Sally'
])
13
->
seeJson
([
14
'created'
=>
true
,
15
]);
16
}
17
}
The seeJson
method converts the given array into JSON, and then verifies that the JSON fragment occurs anywhere within the entire JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present.
Verify Exact JSON Match
If you would like to verify that the given array is an exact match for the JSON returned by the application, you should use the seeJsonEquals
method:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
/**
6
* A basic functional test example.
7
*
8
* @return void
9
*/
10
public
function
testBasicExample
()
11
{
12
$this
->
post
(
'/user'
,
[
'name'
=>
'Sally'
])
13
->
seeJsonEquals
([
14
'created'
=>
true
,
15
]);
16
}
17
}
Authentication
The actingAs
helper method provides a simple way to authenticate a given user as the current user:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
public
function
testApplication
()
6
{
7
$user
=
factory
(
'App\User'
)
->
create
();
8
9
$this
->
actingAs
(
$user
)
10
->
get
(
'/user'
);
11
}
12
}
Custom HTTP Requests
If you would like to make a custom HTTP request into your application and get the full Illuminate\Http\Response
object, you may use the call
method:
1
public
function
testApplication
()
2
{
3
$response
=
$this
->
call
(
'GET'
,
'/'
);
4
5
$this
->
assertEquals
(
200
,
$response
->
status
());
6
}
If you are making POST
, PUT
, or PATCH
requests you may pass an array of input data with the request. Of course, this data will be available in your routes and controller via the Request instance:
1
$response
=
$this
->
call
(
'POST'
,
'/user'
,
[
'name'
=>
'Taylor'
]);
Working With Databases
Lumen also provides a variety of helpful tools to make it easier to test your database driven applications. First, you may use the seeInDatabase
helper to assert that data exists in the database matching a given set of criteria. For example, if we would like to verify that there is a record in the users
table with the email
value of sally@example.com
, we can do the following:
1
public
function
testDatabase
()
2
{
3
// Make call to application...
4
5
$this
->
seeInDatabase
(
'users'
,
[
'email'
=>
'sally@foo.com'
]);
6
}
Of course, the seeInDatabase
method and other helpers like it are for convenience. You are free to use any of PHPUnit’s built-in assertion methods to supplement your tests.
Resetting The Database After Each Test
It is often useful to reset your database after each test so that data from a previous test does not interfere with subsequent tests.
Using Migrations
One option is to rollback the database after each test and migrate it before the next test. Lumen provides a simple DatabaseMigrations
trait that will automatically handle this for you. Simply use the trait on your test class:
1
<?
php
2
3
use
Laravel\Lumen\Testing\DatabaseMigrations
;
4
use
Laravel\Lumen\Testing\DatabaseTransactions
;
5
6
class
ExampleTest
extends
TestCase
7
{
8
use
DatabaseMigrations
;
9
10
/**
11
* A basic functional test example.
12
*
13
* @return void
14
*/
15
public
function
testBasicExample
()
16
{
17
$this
->
get
(
'/foo'
);
18
}
19
}
Using Transactions
Another option is to wrap every test case in a database transaction. Again, Lumen provides a convenient DatabaseTransactions
trait that will automatically handle this:
1
<?
php
2
3
use
Laravel\Lumen\Testing\DatabaseMigrations
;
4
use
Laravel\Lumen\Testing\DatabaseTransactions
;
5
6
class
ExampleTest
extends
TestCase
7
{
8
use
DatabaseTransactions
;
9
10
/**
11
* A basic functional test example.
12
*
13
* @return void
14
*/
15
public
function
testBasicExample
()
16
{
17
$this
->
get
(
'/foo'
);
18
}
19
}
Model Factories
When testing, it is common to need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Lumen allows you to define a default set of attributes for each of your Eloquent models using “factories”. To get started, take a look at the database/factories/ModelFactory.php
file in your application. Out of the box, this file contains one factory definition:
1
$factory
->
define
(
'App\User'
,
function
(
$faker
)
{
2
return
[
3
'name'
=>
$faker
->
name
,
4
'email'
=>
$faker
->
email
,
5
];
6
});
Within the Closure, which serves as the factory definition, you may return the default test values of all attributes on the model. The Closure will receive an instance of the Faker PHP library, which allows you to conveniently generate various kinds of random data for testing.
Of course, you are free to add your own additional factories to the ModelFactory.php
file.
Multiple Factory Types
Sometimes you may wish to have multiple factories for the same Eloquent model class. For example, perhaps you would like to have a factory for “Administrator” users in addition to normal users. You may define these factories using the defineAs
method:
1
$factory
->
defineAs
(
'App\User'
,
'admin'
,
function
(
$faker
)
{
2
return
[
3
'name'
=>
$faker
->
name
,
4
'email'
=>
$faker
->
email
,
5
'admin'
=>
true
,
6
];
7
});
Instead of duplicating all of the attributes from your base user factory, you may use the raw
method to retrieve the base attributes. Once you have the attributes, simply supplement them with any additional values you require:
1
$factory
->
defineAs
(
'App\User'
,
'admin'
,
function
(
$faker
)
use
(
$factory
)
{
2
$user
=
$factory
->
raw
(
'App\User'
);
3
4
return
array_merge
(
$user
,
[
'admin'
=>
true
]);
5
});
Using Factories In Tests
Once you have defined your factories, you may use them in your tests or database seed files to generate model instances using the global factory
function. So, let’s take a look at a few examples of creating models. First, we’ll use the make
method, which creates models but does not save them to the database:
1
public
function
testDatabase
()
2
{
3
$user
=
factory
(
'App\User'
)
->
make
();
4
5
// Use model in tests...
6
}
If you would like to override some of the default values of your models, you may pass an array of values to the make
method. Only the specified values will be replaced while the rest of the values remain set to their default values as specified by the factory:
1
$user
=
factory
(
'App\User'
)
->
make
([
2
'name'
=>
'Abigail'
,
3
]);
You may also create a Collection of many models or create models of a given type:
1
// Create three App\User instances...
2
$users
=
factory
(
'App\User'
,
3
)
->
make
();
3
4
// Create an App\User "admin" instance...
5
$user
=
factory
(
'App\User'
,
'admin'
)
->
make
();
6
7
// Create three App\User "admin" instances...
8
$users
=
factory
(
'App\User'
,
'admin'
,
3
)
->
make
();
Persisting Factory Models
The create
method not only creates the model instances, but also saves them to the database using Eloquent’s save
method:
1
public
function
testDatabase
()
2
{
3
$user
=
factory
(
'App\User'
)
->
create
();
4
5
// Use model in tests...
6
}
Again, you may override attributes on the model by passing an array to the create
method:
1
$user
=
factory
(
'App\User'
)
->
create
([
2
'name'
=>
'Abigail'
,
3
]);
Adding Relations To Models
You may even persist multiple models to the database. In this example, we’ll even attach a relation to the created models. When using the create
method to create multiple models, an Eloquent collection instance is returned, allowing you to use any of the convenient functions provided by the collection, such as each
:
1
$users
=
factory
(
'App\User'
,
3
)
2
->
create
()
3
->
each
(
function
(
$u
)
{
4
$u
->
posts
()
->
save
(
factory
(
'App\Post'
)
->
make
());
5
});
Mocking
Mocking Events
If you are making heavy use of Lumen’s event system, you may wish to silence or mock certain events while testing. For example, if you are testing user registration, you probably do not want all of a UserRegistered
event’s handlers firing, since these may send “welcome” e-mails, etc.
Lumen provides a convenient expectsEvents
method that verifies the expected events are fired, but prevents any handlers for those events from running:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
public
function
testUserRegistration
()
6
{
7
$this
->
expectsEvents
(
'App\Events\UserRegistered'
);
8
9
// Test user registration code...
10
}
11
}
If you would like to prevent all event handlers from running, you may use the withoutEvents
method:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
public
function
testUserRegistration
()
6
{
7
$this
->
withoutEvents
();
8
9
// Test user registration code...
10
}
11
}
Mocking Jobs
Sometimes, you may wish to simply test that specific jobs are dispatched by your controllers when making requests to your application. This allows you to test your routes / controllers in isolation - set apart from your job’s logic. Of course, you can then test the job itself in a separate test class.
Lumen provides a convenient expectsJobs
method that will verify that the expected jobs are dispatched, but the job itself will not be executed:
1
<?
php
2
3
class
ExampleTest
extends
TestCase
4
{
5
public
function
testPurchasePodcast
()
6
{
7
$this
->
expectsJobs
(
'App\Jobs\PurchasePodcast'
);
8
9
// Test purchase podcast code...
10
}
11
}
Note: This method only detects jobs that are dispatched via the
dispatch
global helper function or the$this->dispatch
method from a route or controller. It does not detect jobs that are sent directly toQueue::push
.
Mocking Facades
When testing, you may often want to mock a call to a Lumen facade. For example, consider the following controller action:
1
<?
php
2
3
namespace
App\Http\Controllers
;
4
5
use
Cache
;
6
7
class
UserController
extends
Controller
8
{
9
/**
10
* Show a list of all users of the application.
11
*
12
* @return Response
13
*/
14
public
function
index
()
15
{
16
$value
=
Cache
::
get
(
'key'
);
17
18
//
19
}
20
}
We can mock the call to the Cache
facade by using the shouldReceive
method, which will return an instance of a Mockery mock. Since facades are actually resolved and managed by the Lumen service container, they have much more testability than a typical static class. For example, let’s mock our call to the Cache
facade:
1
<?
php
2
3
class
FooTest
extends
TestCase
4
{
5
public
function
testGetIndex
()
6
{
7
Cache
::
shouldReceive
(
'get'
)
8
->
once
()
9
->
with
(
'key'
)
10
->
andReturn
(
'value'
);
11
12
$this
->
get
(
'/users'
);
13
}
14
}
Note: You should not mock the
Request
facade. Instead, pass the input you desire into the HTTP helper methods such ascall
andpost
when running your test.
Validation
Introduction
Lumen provides several different approaches to validate your application’s incoming data. By default, Lumen’s base controller class uses a ProvidesConvenienceMethods
trait which provides a convenient method to validate incoming HTTP request with a variety of powerful validation rules.
In general, validation in Lumen works exactly like validation in Laravel, so you should consult the full Laravel validation documentation; however, there are a few important differences.
Differences From Laravel
Form Requests
Form requests are not supported by Lumen. If you would like to use form requests, you should use the full Laravel framework.
The $this->validate
Method
The $this->validate
helper which is available in Lumen will always return a JSON response with the relevant error messages. This is in contrast to the Laravel version of the method which will return a redirect response if the request is not an AJAX request. Since Lumen is stateless and does not support sessions, flashing errors to the session is not a possibility. If you would like to use redirects and flashed error data, you should use the full Laravel framework.
Unlike Laravel, Lumen provides access to the validate
method from within Route closures:
1
use
Illuminate\Http\Request
;
2
3
$app
->
post
(
'/user'
,
function
(
Request
$request
)
{
4
$this
->
validate
(
$request
,
[
5
'name'
=>
'required'
,
6
'email'
=>
'required|email|unique:users'
7
]);
8
9
// Store User...
10
});
Of course, you are free to manually create validator instances using the Validator::make
facade method just as you would in Laravel.
The $errors
View Variable
Lumen does not support sessions out of the box, so the $errors
view variable that is available in every view in Laravel is not available in Lumen. Should validation fail, the $this->validate
helper will throw Illuminate\Validation\ValidationException
with embedded JSON response that includes all relevant error messages. If you are not building a stateless API that solely sends JSON responses, you should use the full Laravel framework.