Laravel 5 represents the biggest evolution in Laravel's history. Released in February 2015, it's not just an incremental update—it's a complete reimagining of Laravel's structure and conventions. At ZIRA Software, we've been testing Laravel 5 since beta, and the improvements are game-changing.
What's New in Laravel 5
Major changes:
- Complete directory restructuring
- Middleware replaces filters
- Form Request validation
- Laravel Elixir for assets
- Socialite for OAuth
- Contracts (interfaces)
.envconfiguration- Flysystem integration
- Enhanced scheduler
New Directory Structure
Laravel 4's app folder was flat. Laravel 5 uses PSR-4 autoloading with namespaces:
Laravel 4:
app/
controllers/
models/
views/
...
Laravel 5:
app/
Console/
Events/
Exceptions/
Http/
Controllers/
Middleware/
Requests/
Providers/
User.php
All application code lives in app/ and uses the App namespace:
<?php namespace App\Http\Controllers;
use App\User;
use App\Http\Requests\CreateUserRequest;
class UserController extends Controller {
//
}
Middleware: The Future of Filters
Filters are gone. Middleware is here.
Laravel 4 Filter:
// filters.php
Route::filter('auth', function() {
if (Auth::guest()) {
return Redirect::guest('login');
}
});
// routes.php
Route::get('dashboard', ['before' => 'auth', function() {
//
}]);
Laravel 5 Middleware:
// app/Http/Middleware/Authenticate.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
protected $auth;
public function __construct(Guard $auth) {
$this->auth = $auth;
}
public function handle($request, Closure $next) {
if ($this->auth->guest()) {
return redirect()->guest('login');
}
return $next($request);
}
}
Registering middleware:
// app/Http/Kernel.php
protected $middleware = [
\App\Http\Middleware\CheckForMaintenanceMode::class,
];
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];
Using middleware:
Route::get('dashboard', ['middleware' => 'auth', function() {
//
}]);
// Or in controllers
public function __construct() {
$this->middleware('auth');
$this->middleware('admin', ['only' => ['create', 'store']]);
}
Terminable middleware:
class StartSession implements TerminableMiddleware {
public function handle($request, Closure $next) {
return $next($request);
}
public function terminate($request, $response) {
// Perform action after response is sent to browser
session()->save();
}
}
Form Request Validation
Move validation logic out of controllers:
Laravel 4:
public function store() {
$validator = Validator::make(Input::all(), [
'name' => 'required|max:255',
'email' => 'required|email|unique:users'
]);
if ($validator->fails()) {
return Redirect::back()->withErrors($validator)->withInput();
}
// Store user
}
Laravel 5 Form Request:
// app/Http/Requests/CreateUserRequest.php
<?php namespace App\Http\Requests;
class CreateUserRequest extends Request {
public function authorize() {
return true; // or check if user can create users
}
public function rules() {
return [
'name' => 'required|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:6|confirmed'
];
}
public function messages() {
return [
'name.required' => 'A name is required',
'email.unique' => 'This email is already registered'
];
}
}
Controller becomes clean:
use App\Http\Requests\CreateUserRequest;
public function store(CreateUserRequest $request) {
// Validation passed automatically!
$user = User::create($request->all());
return redirect()->route('users.show', $user->id);
}
Environment Configuration
Laravel 5 uses .env files for configuration:
# .env
APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:random-key-here
DB_HOST=localhost
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret
CACHE_DRIVER=redis
SESSION_DRIVER=file
QUEUE_DRIVER=beanstalkd
Access in config files:
// config/database.php
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
],
Benefits:
- Environment-specific settings without multiple config files
- Sensitive data stays out of version control
- Easy deployment to different environments
Laravel Elixir
Asset compilation made easy:
// gulpfile.js
var elixir = require('laravel-elixir');
elixir(function(mix) {
mix.sass('app.scss')
.coffee('app.coffee')
.scripts([
'jquery.js',
'app.js'
], 'public/js/bundle.js')
.version(['css/app.css', 'js/bundle.js']);
});
Run with:
gulp
gulp watch
gulp --production
Features:
- Sass/Less compilation
- CoffeeScript compilation
- JavaScript concatenation/minification
- CSS minification
- File versioning for cache busting
- Browser testing with PHPUnit/PHPSpec
Socialite: OAuth Made Simple
First-party OAuth integration:
composer require laravel/socialite
Configuration:
// config/services.php
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => 'http://yourapp.com/auth/github/callback',
],
Implementation:
// Redirect to GitHub
public function redirectToProvider() {
return Socialite::driver('github')->redirect();
}
// Handle callback
public function handleProviderCallback() {
$user = Socialite::driver('github')->user();
// $user->token;
// $user->getId();
// $user->getNickname();
// $user->getName();
// $user->getEmail();
// $user->getAvatar();
$authUser = User::firstOrCreate([
'email' => $user->getEmail(),
'name' => $user->getName(),
'github_id' => $user->getId(),
'avatar' => $user->getAvatar()
]);
Auth::login($authUser, true);
return redirect()->to('/dashboard');
}
Supported providers:
- GitHub
- Bitbucket
Contracts (Interfaces)
Laravel 5 introduces Contracts - a set of interfaces defining core services:
<?php namespace App\Repositories;
use Illuminate\Contracts\Cache\Repository as Cache;
class UserRepository {
protected $cache;
public function __construct(Cache $cache) {
$this->cache = $cache;
}
public function all() {
return $this->cache->remember('users.all', 60, function() {
return User::all();
});
}
}
Benefits:
- Loose coupling
- Easy testing with mocks
- Clear interface definitions
- IDE autocomplete for facades
Flysystem Integration
Unified API for file storage:
// Local
Storage::disk('local')->put('file.txt', 'Contents');
// Amazon S3
Storage::disk('s3')->put('file.txt', 'Contents');
// Rackspace
Storage::disk('rackspace')->put('file.txt', 'Contents');
// Same API for all!
$contents = Storage::get('file.txt');
$exists = Storage::exists('file.txt');
Storage::delete('file.txt');
Configuration:
// config/filesystems.php
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
's3' => [
'driver' => 's3',
'key' => env('S3_KEY'),
'secret' => env('S3_SECRET'),
'region' => env('S3_REGION'),
'bucket' => env('S3_BUCKET'),
],
],
Enhanced Task Scheduling
Schedule recurring tasks in code, not cron:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule) {
// Run command every hour
$schedule->command('emails:send')->hourly();
// Run at specific time
$schedule->command('reports:generate')
->dailyAt('13:00');
// Run on weekdays
$schedule->command('backup:run')
->weekdays()
->at('02:00');
// Run closure
$schedule->call(function() {
DB::table('recent_users')->delete();
})->daily();
}
Single cron entry:
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
Method Injection
Inject dependencies into controller methods:
use App\Repositories\UserRepository;
public function show(UserRepository $users, $id) {
$user = $users->find($id);
return view('users.show', compact('user'));
}
Laravel automatically resolves dependencies from the service container.
Improved Authentication
Password reset improvements:
php artisan make:auth
Generates complete authentication scaffolding:
- Login/Registration views and controllers
- Password reset flow
- Email verification templates
Social authentication with Socialite:
Route::get('auth/{provider}', 'Auth\AuthController@redirectToProvider');
Route::get('auth/{provider}/callback', 'Auth\AuthController@handleProviderCallback');
Collections Enhancements
New powerful collection methods:
$collection = collect([1, 2, 3, 4, 5]);
// whereIn
$filtered = $collection->whereIn('id', [1, 3, 5]);
// whereNotIn
$filtered = $collection->whereNotIn('id', [2, 4]);
// contains with callback
$contains = $collection->contains(function($item) {
return $item > 3;
});
Migration Guide
From Laravel 4 to Laravel 5
- Install fresh Laravel 5:
composer create-project laravel/laravel myapp
- Move models:
# Move to app/ and add namespace
<?php namespace App;
class User extends Model {
//
}
- Update controllers:
<?php namespace App\Http\Controllers;
class UserController extends Controller {
//
}
-
Convert filters to middleware
-
Update configuration:
Create .env file and update config/ files
- Update views:
// Laravel 4
{{ Form::open() }}
// Laravel 5
{!! Form::open() !!} // Unescaped
{{ $variable }} // Escaped by default
Best Practices
1. Use Form Requests for validation
public function store(CreatePostRequest $request) {
// Already validated
}
2. Organize with Repositories
App\Repositories\
UserRepository.php
PostRepository.php
3. Use Events for decoupling
Event::fire(new UserRegistered($user));
4. Leverage Middleware
$this->middleware('auth');
$this->middleware('admin', ['except' => 'index']);
5. Environment-specific configuration
// .env for secrets
// config/ for structure
Conclusion
Laravel 5 is a mature, production-ready framework that sets new standards for PHP development. The improvements in code organization, middleware architecture, and developer experience make it the best Laravel release yet.
At ZIRA Software, all new projects start with Laravel 5. The migration from Laravel 4 requires effort, but the benefits are worth it.
Ready to build with Laravel 5? Contact ZIRA Software to discuss your next project, migration strategy, or Laravel training for your team.