Getting the basis right

Laravel Montréal #2 - July 17th, 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 contacts --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

Installing homestead


vagrant box add laravel/homestead



git clone https://github.com/laravel/homestead.git Homestead

The config file


---
ip: "192.168.10.10"
memory: 2048
cpus: 1

authorize: /Users/benjamingonzalez/.ssh/id_rsa.pub

keys:
    - /Users/benjamingonzalez/.ssh/id_rsa

folders:
    - map: /Applications/MAMP/htdocs/contacts
      to: /home/vagrant/code/contacts

sites:
    - map: contacts.app
      to: /home/vagrant/code/contacts/public

variables:
    - key: APP_ENV
      value: local

Routing

Simple routing


// Simple resource routing
Route::resource('/', 'ContactsController');

		

Listing Routes


php artisan routes

	

Controllers

Resource Controllers


php artisan controller:make ContactsController

	

Route::resource('/', 'ContactsController');

	

class ContactsController extends BaseController {

	public function index() {}          // GET    /
	public function create() {}         // GET    /create
	public function store() {}          // POST   /
	public function show($id) {}        // GET    /1
	public function edit($id) {}        // GET    /1/edit
	public function update($id) {}      // PUT    /1
	public function destroy($id) {}     // DELETE /1

}

	

php artisan routes

	

Database setup

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'  => 'contacts',
			'username'  => 'homestead',
			'password'  => 'secret'
			'charset'   => 'utf8',
			'collation' => 'utf8_unicode_ci',
			'prefix'    => '',
		),
	…

		

return array(
	'default' => 'mysql',

	'connections' => array(
		'mysql' => array(
			'driver'    => 'mysql',
			'host'      => 'localhost',
			'database'  => $_ENV['MYSQL_DB'],
			'username'  => $_ENV['MYSQL_USER'],
			'password'  => $_ENV['MYSQL_PASS'],
			'charset'   => 'utf8',
			'collation' => 'utf8_unicode_ci',
			'prefix'    => '',
		),
	…

		

Secret Environment Variables


return array(
	'MYSQL_DB' => 'contacts',
	'MYSQL_USER' => 'homestead',
	'MYSQL_PASS' => 'secret',
);

		

Migrations

Preparing


php artisan migrate:make create_contacts_table --create="contacts"

		

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('name', 50);
		$table->string('email')->nullable()->unique();
		$table->string('telephone')->nullable()->unique();
		$table->boolean('active')->default(true);
		$table->timestamps();
	});
}


public function down()
{
	Schema::drop('posts');
}

		

Run Migrations


php artisan migrate

		

Ooops!


php artisan migrate:rollback

			

Models

Eloquent


class Contact extends Eloquent {}

	

Getting all results



    /**
     * Display a listing of contacts
     *
     * @return Response
     */
    public function index()
    {
        $contacts = Contact::all();

        return View::make('contacts.index', compact('contacts'));
    }

	

Displaying a new contact form



    /**
     * Show the form for creating a new contact
     *
     * @return Response
     */
    public function create()
    {
        return View::make('contacts.create');
    }

	

Storing a new contact



    /**
     * Store a newly created contact in storage.
     *
     * @return Response
     */
    public function store()
    {
        $validator = Validator::make($data = Input::all(), Contact::$rules);

        if ($validator->fails())
        {
            return Redirect::back()->withErrors($validator)->withInput();
        }

        Contact::create($data);

        return Redirect::route('contacts.index');
    }


	

Showing a contact



    /**
     * Display the specified contact.
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        $contact = Contact::findOrFail($id);

        return View::make('contacts.show', compact('contact'));
    }


	

Edit form



    /**
     * Show the form for editing the specified contact.
     *
     * @param  int  $id
     * @return Response
     */
    public function edit($id)
    {
        $contact = Contact::find($id);

        return View::make('contacts.edit', compact('contact'));
    }


	

Updating the contact



    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        $contact = Contact::findOrFail($id);

        $validator = Validator::make($data = Input::all(), Contact::$rules);

        if ($validator->fails())
        {
            return Redirect::back()->withErrors($validator)->withInput();
        }

        $contact->update($data);

        return Redirect::route('contacts.index');
    }


	

Deleting a contact

	

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function destroy($id)
    {
        Contact::destroy($id);

        return Redirect::route('contacts.index');
    }

}

	

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

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

Form Handing

Building Forms


{{ Form::open() }}

	{{ Form::label('name') }}
	{{ Form::text('name', 'default', array('class'=>'form-control') }}

	{{ Form::label('email') }}
	{{ Form::email('email') }}

	{{ Form::label('phone') }}
	{{ Form::email('phone') }}

	{{ Form::checkbox('is_active', 1, null, array('tabindex'=> 4 ) }}
		{{ Form::label('is_active', 'Yes') }}

	{{ Form::submit('Send it!')}}

{{ Form::close() }}


	

Extracting forms !


Creating a contact

{{ Form::open(['method' => 'POST', 'route' => ['store' => $contact->id]]) }} @include('contacts.partials.form') {{ Form::close() }}

Updating a contact

{{ Form::open(['method' => 'PATCH', 'route' => ['update' => $contact->id]]) }} @include('contacts.partials.form') {{ Form::close() }}

Form Model Binding


$contact = Contact::findOrFail($id);
return View::make('contacts.edit', compact('contact'));

	

{{ Form::model($contact, array('route'=>array('update', $contact->id), 'method'=>'PUT') ) }}

@include('contacts.partials.form')

{{ Form::close() }}

	

Validation

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



$validator = Validator::make( $data, Contact::$rules );


	

	
	public static $rules = array(...);


Validation Rules


$data = array(
	'contact' => 'Benjamin Gonzalez',
	'email'  => 'benjamin.rosell@gmail.com',
));

$rules = array(
	'contact'        => 'required|min:5',
	'email'          => 'required|email'
);

	

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 && Clockwork

Caching

Mail

Queues

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!