PHP Québec Meetup - July 3rd, 2014
A free, open source, MVC web-application framework
2011 (v1.0) — current (v4.2.05)
Simple to use, elegant, and expressive API
Emphasis on testability
Great documentation
Package-based dependency management
« Any time that I have to do something that is a pain... it puts a seed in my head to work it into Laravel »
- Taylor Otwell
full of static methods...
a restrictive framework
the absolute best framework
always the best solution
composer create-project laravel/laravel my-project --prefer-dist
Command line tool to make working with Laravel easier.
php artisan
php artisan serve --port 8000
Route::get('/', function() { return 'Hello World!'; });
Route::post('/', function() { // insert whatever code here ! });
// pass parameters via URI
Route::get('/hello/{name}', function($name)
{
return 'Hello, ' . $name;
});
// escape output for safety
Route::get('/hello/{name}', function($name)
{
return 'Hello, ' . e($name);
});
// filter the URI data
Route::get('/hello/{name}', function($name)
{
return 'Hello, ' . e($name);
})->where('name','[A-za-z]+');
// make the param optional
Route::get('/hello/{name?}', function($name='stranger')
{
return 'Hello, ' . e($name);
})->where('name','[A-za-z]+');
php artisan routes
Route::get('hello/{name?}', 'HelloController@showWelcome');
class HelloController extends BaseController {
public function showWelcome($name='stranger')
{
return 'Hello, '. e($name) . '!';
}
}
Route::controller('users', 'UsersController');
class UsersController extends BaseController {
public function getIndex() {
// GET http://domain.com/users
}
public function postProfile() {
// POST http://domain.com/users/profile
}
}
php artisan controller:make PostsController
Route::resource('posts', 'PostsController');
class PostsController extends BaseController {
public function index() {} // GET /posts
public function create() {} // GET /posts/create
public function store() {} // POST /posts
public function show($id) {} // GET /posts/1
public function edit($id) {} // GET /posts/1/edit
public function update($id) {} // PUT /posts/1
public function destroy($id) {} // DELETE /posts/1
}
php artisan routes
class HelloController extends BaseController {
public function showWelcome($name='stranger')
{
return View::make('hello', array('name'=>$name) );
}
}
class HelloController extends BaseController {
public function showWelcome($name='stranger')
{
return View::make('hello', compact('name') );
}
}
<html>
<body>
<p>Hello, <?php echo e($name); ?>!</p>
</body>
</html>
<html>
<body>
<p>Hello, {{{ $name }}}!</p>
</body>
</html>
<html>
<body>
// echo and escape output
Hello {{ $name }}! Hello {{{ $html }}}!
</body>
</html>
@if( $name=='Bob' )
Good to see you, Bob!
@elseif( $name=='Mary' )
Howya doing, Mary!
@else
Hey, where did Bob go?
@endif
@unless( $signed_in )
You are not signed in.
@endunless
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
This is user {{ $user->id }}
@endforeach
@while (true)
Make it stop!
@endwhile
<html>
<body>
@include('header') {{-- app/views/header.blade.php --}}
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content', 'Default content')
</div>
</body>
</html>
@extends('layouts.master')
@section('sidebar')
@parent
This is appended to the master sidebar.
@stop
@section('content')
This is my body content.
@stop
Out-of-the-box support for:
return array(
'default' => 'mysql',
'connections' => array(
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'my-project',
'username' => 'db_user',
'password' => 's3creT_pa5sw0rD'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
…
return array(
'default' => 'mysql',
'connections' => array(
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'my-project',
'username' => $_ENV['MYSQL_USER'],
'password' => $_ENV['MYSQL_PASS'],
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
…
return array(
'MYSQL_USER' => 'dbuser',
'MYSQL_PASS' => 's3creT_pa5sw0rD',
);
php artisan migrate:install
php artisan migrate:make create_posts_table --create="posts"
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}
public function up() { Schema::create('posts', function(Blueprint $table) { $table->increments('id'); $table->string('title', 50); $table->text('text')->nullable(); $table->boolean('active')->default(true); $table->timestamps(); // created_at and updated_at }); }
public function down() { Schema::drop('posts'); }
php artisan migrate
php artisan migrate:rollback
class Post extends Eloquent {}
$post = Post::create(
array(
'title' => 'My First Post',
'text' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit',
'active' => true,
)
);
$post = Post::find(1);
$post = Post::findOrFail(1);
$posts = Post::where('id','>',3)->get(); $post = Post::where('title','LIKE','laravel')->first();
$posts = Post::all();
$post = Post::find(1); echo $post->title; echo $post->created_at; // uses Carbon package for date handling
$posts = Post::all(); foreach( $posts as $post ) { echo $post->title; }
$post = Post::find(1); $post->delete();
Post::destroy(1); Post::destroy(1,2,3);
$lastWeek = Carbon::now()->subWeek(); Post::where('created_at','<', $lastWeek)->delete();
class Post extends Eloquent { public function getTitleAttribute($value) { return ucwords($value); } }
$post = Post::create(array( 'title' => 'my first post' )); echo $post->title; // "My First Post"
class Post extends Eloquent {
public function setTextAttribute($value)
{
$value = trim($value);
$this->attributes['text'] = $value;
// not limited to modifying the one attribute
$this->attributes['excerpt'] = substr($value,0,97) . '...';
}
}
class Comment extends Eloquent { // comments table has an `author_id` field public function author() { return $this->hasOne('Author'); } }
$author = Comment::find(1)->author;
class Author extends Eloquent { // authors table needs a `post_id` column public function comment() { return $this->belongsTo('Comment'); } }
$title = Author::find(1)->comment->title;
class Book extends Eloquent { // books table has `author_id` column public function author() { return $this->belongsTo('Author'); } } class Author extends Eloquent { public function books() { return $this->hasMany('Book'); } }
$my_books = Author::where('firstname','=','Benjamin')->books;
class Book extends Eloquent {
public function tags()
{
return $this->belongsToMany('Tag');
}
}
class Tag extends Eloquent {
public function books()
{
return $this->belongsToMany('Book');
}
}
Schema::create('book_tag', function(Blueprint $table) {
$table->increments('id');
$table->integer('book_id')->unsigned()->index();
$table->integer('tag_id')->unsigned()->index();
});
Book::with('author')->get();
Book::with('author','tags')->get();
Tag::with('books.author')->get();
{{ Form::open() }}
{{ Form::label('title') }}
{{ Form::text('title', 'default', array('class'=>'form-control') }}
{{ Form::label('text', 'Enter the full text:') }}
{{ Form::textarea('text', 'default', array('placeholder'=>'Enter the text') }}
{{ Form::email('email') }}
{{ Form::checkbox('is_active', 1, null, array('tabindex'=>4) }}
{{ Form::label('is_active', 'Yes') }}
{{ Form::submit('Send it!')}}
{{ Form::close() }}
$input = Input::get('field','default');
$input = Input::only('firstname','lastname');
$input = Input::except('credit_card');
$input = Input::get('choices.0.name');
$input = Input::all();
$input = Input::file('upload_field');
$book = Book::findOrFail($id);
return View::make('books.edit', compact('book'));
{{ Form::model($book, array('route'=>array('books.update', $book->id), 'method'=>'PUT') ) }}
{{ Form::text('title')}}
{{ Form::textarea('description')}}
…
{{ Form::close() }}
$data = array(
'title' => 'My First Post',
'text' => 'Lorem hipster YOLO sic amet, semiotics banh mi flexitarian.',
));
$rules = array(
'title' => 'required|min:5',
'description' => 'required|max:100'
);
$validator = Validator::make( $data, $rules );
if ( $validator->fails() ) // ->passes()
{
$messages = $validator->messages();
…
}
if ( $messages->has('email') )
{
echo $messages->first('email', ':message
');
}
$messages->get('field');
$messages->all();
$rules = array(
'firstname' => 'required',
'lastname' => 'alpha',
'email' => 'email',
'age' => 'integer|between:18,65',
'agree' => array('required','accepted'),
'website' => 'url',
'password' => 'confirmed', // implies matching 'password_confirmation' data
'company_id' => 'exists:companies,id',
'gender' => 'in:male,female',
'photo' => 'mimes:jpeg,png|max:100', // kilobytes
'postalcode' => 'regex:^[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]$'
);
Built on top of Monolog
Log::info('This is some useful information.');
Log::info('Information with context', array('context' => 'Other helpful information'));
Log::warning('Something could be going wrong.');
Log::error('OMGWTFBBQ!');
app/storage/logs/laravel.log
app/config/cache.php
Cache::put('key', 'value', $minutes);
$expiresAt = Carbon::now()->addHours(10);
Cache::put('key', 'value', $expiresAt);
if (Cache::has('key')) { … }
$value = Cache::get('key');
$value = Cache::get('key', 'default');
$value = Cache::get('key', function() { return 'default'; });
$value = Cache::forever('key', 'value');
Cache::forget('key');
app/config/mail.php
Mail::send('emails.view', $data, function($message)
{
$message->to('Benjamin@example.com', 'Benjamin Gonzalez')
->subject('Welcome!');
});
Mail::queue('emails.view', $data, function($message)
{
$message->to('Benjamin@example.com', 'Benjamin Gonzalez')
->subject('Welcome!');
});
Mail::later($seconds, 'emails.view', $data, function($message)
{
$message->to('Benjamin@example.com', 'Benjamin Gonzalez')
->subject('Welcome!');
});
app/config/queue.php
$data = array('foo' => 'bar');
Queue::push('MyJob', $data);
class MyJob {
public function fire($job, $data)
{
…
}
}