Laravel 101

PHP Québec Meetup - July 3rd, 2014

Benjamin Gonzalez

  • Web Developer back-end and more recently front-end
  • Involved in PHP since 2006 with a few years off for good behaviour
  • CTO for InfoPrimes, founder of Laravel Montréal we meet every month, you are welcome :)
  • @BenjaminRosell at Github and Twitter … also on LinkedIn

Laravel

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

laravel.com

« 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

Laravel is not

full of static methods...

a restrictive framework

the absolute best framework

always the best solution

Getting Started

Prerequisites

  • PHP 5.4.x
  • Mcrypt extension
  • JSON extension
  • Composer getcomposer.org and install it globally!

Your First Project

Starting a New Project


composer create-project laravel/laravel my-project --prefer-dist

	

Git it under control

Project Structure

Project Folders

  • app < your stuff
  • bootstrap < startup stuff
  • public < static stuff: img, js, css, etc.
  • composer.json < other people's stuff you want
  • vendor < other people's stuff

Application Folders

  • app
    • commands
    • config
    • controllers
    • database
    • lang
    • models
    • start
    • storage
    • tests
    • views
    • filters.php
    • routes.php

Artisan

Artisan

Command line tool to make working with Laravel easier.


php artisan

		

Built-in Server


php artisan serve --port 8000

		

Homestead

Software installed

  • Ubuntu 14.04
  • PHP 5.5
  • Nginx
  • MySQL
  • Postgres
  • Node (With Bower, Grunt, and Gulp)
  • Redis
  • Memcached
  • Beanstalkd
  • Laravel Envoy
< make sure you have vagrant and virtualbox installed

Routing

Basic Routing


Route::get('/', function() {
	return 'Hello World!';
});


Route::post('/', function() {
	// insert whatever code here !
});

	

Route Parameters


// 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]+');

		

Listing Routes


php artisan routes

	

Controllers

Controller Routing


Route::get('hello/{name?}', 'HelloController@showWelcome');

	

class HelloController extends BaseController {

	public function showWelcome($name='stranger')
	{
		return 'Hello, '. e($name) . '!';
	}

}

	

RESTful Controllers


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
	}

}

	

Resource Controllers


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

	

Views

Passing Data to Views


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>

		

Blade Templating

Outputting Data


<html>
	<body>
		// echo and escape output
		Hello {{ $name }}!  Hello {{{ $html }}}!
	</body>
</html>

	 

If Statements


@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

	 

Loops


@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

Layouts


<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

Database

Supported Databases

Out-of-the-box support for:

  • MySQL
  • Postgres
  • SQLite
  • SQL Server

Configuration


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'    => '',
		),
	…

		

Secret Environment Variables


return array(
	'MYSQL_USER' => 'dbuser',
	'MYSQL_PASS' => 's3creT_pa5sw0rD',
);

		

Migrations

Preparing


php artisan migrate:install

php artisan migrate:make create_posts_table --create="posts"

		

Migration File


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()
	{
		//
	}

}

		

Schema Building


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');
}

		

Run Migrations


php artisan migrate

		

Ooops!


php artisan migrate:rollback

			

Models

Eloquent


class Post extends Eloquent {}

	

Create a New Model


$post = Post::create(
	array(
		'title'  => 'My First Post',
		'text'   => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit',
		'active' => true,
	)
);

		

Retrieving


$post = Post::find(1);


$post = Post::findOrFail(1);


$posts = Post::where('id','>',3)->get();
$post = Post::where('title','LIKE','laravel')->first();


$posts = Post::all();

		

Using


$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;
}

		

Deleting


$post = Post::find(1);
$post->delete();


Post::destroy(1);
Post::destroy(1,2,3);


$lastWeek = Carbon::now()->subWeek();
Post::where('created_at','<', $lastWeek)->delete();

		

Accessors & Mutators

Accessors


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"

		

Mutators


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) . '...';

		}

}

		

Relationships

One-to-One


class Comment extends Eloquent {

	// comments table has an `author_id` field

	public function author()
	{
		return $this->hasOne('Author');
	}

}


$author = Comment::find(1)->author;

		

Inverse


class Author extends Eloquent {

	// authors table needs a `post_id` column

	public function comment()
	{
		return $this->belongsTo('Comment');
	}

}


$title = Author::find(1)->comment->title;

		

One-to-Many


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;

	

Many-to-Many


class Book extends Eloquent {
	public function tags()
	{
		return $this->belongsToMany('Tag');
	}
}

class Tag extends Eloquent {
	public function books()
	{
		return $this->belongsToMany('Book');
	}
}

		

Pivot Table


Schema::create('book_tag', function(Blueprint $table) {
	$table->increments('id');
	$table->integer('book_id')->unsigned()->index();
	$table->integer('tag_id')->unsigned()->index();
});

		

Eager Loading


Book::with('author')->get();

	

Book::with('author','tags')->get();

	

Tag::with('books.author')->get();

	

Form Handing

Building Forms


{{ 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() }}


	

Handling Input


$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');

	

Form Model Binding


$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() }}

	

Validation

Validation Rules


$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'
);

	

Validation


$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();

	

Validation Rules


$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]$'
);

	

Logging

Logging

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

Caching

Cache Configuration

  • app/config/cache.php
  • Drivers:
    • file
    • database
    • apc
    • memcached
    • redis
    • array

Cache Usage


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');

	

Mail

Mail Configuration

  • Built on top of SwiftMailer
  • app/config/mail.php
  • Drivers:
    • smtp
    • mail
    • sendmail

Mail Usage


Mail::send('emails.view', $data, function($message)
{
	$message->to('Benjamin@example.com', 'Benjamin Gonzalez')
		->subject('Welcome!');
});

	

Queueing Mail


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!');
});

	

Queues

Queue Configuration

Queue Usage


$data = array('foo' => 'bar');

Queue::push('MyJob', $data);

	

class MyJob {

	public function fire($job, $data)
	{
		…
	}

}

	

Authentication

Events

Localization

Packages

Laravel Forge

Instant PHP platforms on the cloud

  • Works with:
    • Linode
    • Digital Ocean
    • AWS
    • Rackspace
  • Super simple to set up :
    • Create an account
    • Add Api keys
    • Deploy !

Laravel Forge

  • Main features :
    • Same setup as Laravel Homestead same local and remote environment
    • Push to deploy Github or Bitbucket
    • Does not depend on Laravel ... Tuned for Laravel, framework agnostic
    • Unlimited servers, domains, subdomains, etc...

Further Reading

That’s All Folks!