Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Laravel for Rapid Development

Rob Allen
April 13, 2018

Using Laravel for Rapid Development

This workshop introduces using Laravel’s opinionated rapid development concepts to create a real, working website. You’ll use the Blade templating engine, database access through Eloquent, command line using Artisan and acceptance testing using Dusk. Attendees will leave the session knowing how to quickly and easily create a simple, secure web application using Laravel.

This tutorial was presented at PHP Yorkshire, April 2018

Rob Allen

April 13, 2018
Tweet

More Decks by Rob Allen

Other Decks in Technology

Transcript

  1. Today's plan • Getting started with Laravel • Creating pages

    • Databases • Authentication • Lunch! Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  2. Laravel Elegant applications delivered at warp speed. • Created by

    Taylor Otwell • Full stack framework • Focussed on rapid development • MVC structure • CLI tooling with artisan Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  3. What do you get? • Routing • DI Container •

    Database ORM • Authentication • Caching • Console • Cookies • Encryption • Events • Forms • etc! Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  4. Why Laravel? • Everything in the box • Opinionated •

    Convention over configuration • artisan CLI tool • Ecosystem • Great community • Laracasts Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  5. Ecosystem • Cashier - subscription billing • Dusk - browser

    tests • Echo - broadcasting over sockets • Envoy - task runner • Horizon - Redis queue integration & dashboard • Passport - API authentication • Scout - Full-text search • Socialite - external OAuth authentication • Envoyer - Deployment (paid) • Spark - SAAS scaffolding (paid) Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  6. Running Laravel apps • artisan serve - use built-in PHP

    server • Homestead - local Vagrant setup • Forge - server manager and deployment system Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  7. Installation 1.Install Laravel Installer: composer global require "laravel/installer" 2.Create a

    new project: laravel new my-project-name 3.Run using local webserver: cd my-project-name && php artisan serve Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  8. Directory structure . ├── app/ ├── routes/ ├── bootstrap/ ├──

    storage/ ├── config/ ├── tests/ ├── database/ ├── artisan ├── public/ ├── .env │ ├── index.php ├── composer.json ├── resources/ ├── composer.lock │ ├── assets/ └── package.json │ ├── lang/ │ └── views/ Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  9. app/ └─ app/ ├── Console/ ├── Exceptions/ ├── Http/ │

    ├── Controllers/ │ │ ├── Auth/ │ │ └── Controller.php │ ├── Middleware/ │ └── Kernel.php ├── Providers/ └── User.php Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  10. Configuration config/ holds a set of PHP files; each returns

    an array. // config/app.php <?php return [ 'name' => env('APP_NAME', 'Laravel'), 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // etc... ]; Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  11. Routing A route is defined in routes/web.php & consists of:

    • HTTP method • URI • An action (closure or class method) Route::get('/hello', function () { return 'Hello World'; }); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  12. Router methods Route::get('/hello', $callable); Route::post('/hello', $callable); Route::put('/hello', $callable); Route::patch('/hello', $callable);

    Route::delete('/hello', $callable); Route::options('/hello', $callable); Route::any('/hello', $callable); Route::match(['get', 'post'], '/hello', $callable); Route::redirect('/bonjour', '/hello', 301); Route::view('/hello', 'route-name'); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  13. URI pattern • Literal string match Route::get('/hello', function () {…});

    • Parameters are wrapped in { } $app->get('/hello/{name}', function ($name) {…}); • Optional parameters end with ? $app->get('/hello/{name?}', function ($name='Rob') {… • Constrain parameters via Regex $app->get('/hello/{name}', …) ->where('name', '[A-Za-z]+'); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  14. Routes can be named Assign a name to a route

    so you can generate URLs from it $app->get('/hello/{name}', …)->name('hello'); // Generating URLs... $url = route('hello', ['name' => 'Rob']); // Generating Redirects... return redirect()->route('hello', ['name' => 'Rob']); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  15. Controllers & actions In addition to closures, you can specify

    an action method in a controller: $app->get('/hello/{name}', 'UserController@profile'); UserController: The \App\Http\Controllers\UserController class profile: The profile() method in UserController Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  16. Actions • Receive route parameters • Can optionally receive Request

    object • Manage business logic operations • Return a Response object or a View object Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  17. Actions <?php namespace App\Http\Controllers; class UserController extends Controller { public

    function profile($name) { // grab $user from the model layer return view('user/profile', ['user' => $user]); } } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  18. Blade templates • Call view() to render template: • Store

    templates in resources/views directory • Use the .blade.php file extension Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  19. Action template @extends('layout') @section('content') <h1>User Profile</h1> <ul> @foreach ($user->hobbies as

    $hobby) <li>{{ $hobby }}</li> @endforeach </ul> @endsection Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  20. Print variables @extends('layout') @section('content') <h1>User Profile</h1> <ul> @foreach ($user->hobbies as

    $hobby) <li>{{ $hobby }}</li> @endforeach </ul> @endsection Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  21. Control statements @extends('layout') @section('content') <h1>User Profile</h1> <ul> @foreach ($user->hobbies as

    $hobby) <li>{{ $hobby }}</li> @endforeach </ul> @endsection Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  22. Template inheritance @extends('layout') @section('content') <h1>User Profile</h1> <ul> @foreach ($user->hobbies as

    $hobby) <li>{{ $hobby }}</li> @endforeach </ul> @endsection Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  23. Template inheritance • For cohesive look and feel • includes

    default CSS, JS & structural HTML • Build a base skeleton • Define sections for children to override • Each child chooses which template to inherit from Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  24. Base layout <!-- resources/views/layouts/app.blade.php: --> <html> <head> <link rel="stylesheet" href="style.css"

    /> <title>@yield('title', 'Site name')</title> </head> <body> @yield('content') </body> </html> Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  25. Simple Todo List Our todo list will display a list

    of items to be done. Item properties: • title: text of what's to be done • doby: date when it must be done by • completed: has it been done? The first page will list all our todos… Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  26. Eloquent • Simple to use Active Record implementation • Each

    table has a Model • Conventions: • User model is connected to the users table • Each table has a primary key call id • created_at and updated_at columns exist on your table Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  27. Model $ php artisan make:model User Model created successfully. Creates

    App\User: <?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  28. Fetching data Use all() to quickly grab all records: $users

    = User::all(); foreach($users as $user) { echo $user->name; } or use the fluent interface to build your query: $users = User::where('deleted', 0) ->orderBy('name', 'desc') ->take(20) ->get(); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  29. Fetching a single record Use find() or first(): $user =

    User::find(1); // or $rob = User::where('username', 'akrabat')->first(); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  30. Inserting/updating Create a new Model instance and call save() on

    it: $user = new User(); $user->username = 'geeh'; $user->save(); Calling save() on a retrieved model will update the database: $user = User::where('username', 'geeh')->first(); $user->name = 'Gary'; $user->save(); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  31. Timesavers Fetch a user or instantiate if it doesn't exist:

    $user = User::firstOrNew(['username' => 'geeh']); Update or create a new record if it doesn't exist: $user = User::updateorCreate( ['username' => 'geeh'], // where clause ['name' => 'Gary'] // data to update ); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  32. Deleting Find a Model and call delete() on it: $user

    = User::where('username', 'geeh')->first(); $user->delete(); Or, call destroy() if you know the primary key: User::destroy(2); Or, delete() on a query result: User::where('active', 0)->delete(); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  33. Soft delete Create a deleted_at column in your table and

    Eloquent will automatically exclude these records from your queries: class User extends Model { use SoftDeletes; protected $dates = ['deleted_at']; } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  34. Relationships Relationships are defined as methods on your model Eloquent

    supports: • One to one • One to many • Many to many Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  35. One to one relationships A user has an address. Links

    via addresses:user_id. class User extends Model { public function address() { return $this->hasOne('App\Address'); } } // usage: $address = User::find(1)->address; Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  36. One to one relationships Reverse: class Address extends Model {

    public function user() { return $this->belongsTo('App\User'); } } // usage: $user = Address::find(1)->user; Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  37. One to many relationships A user has many talks. Links

    via talks:user_id. class User extends Model { public function talks() { return $this->hasMany('App\Talk'); } } // usage: $talks = User::find(1)->talks; foreach ($talks as $talk) { /* … */ } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  38. One to many relationships Reverse: class Talk extends Model {

    public function user() { $this->belongsTo('App\User'); } } // usage: $user = Address::find(1)->user; Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  39. Many to many relationships A user attends many events. Links

    via events_users. class User extends Model { public function events() { return $this->belongsToMany('App\Event'); } } // usage: $events = $user->events()->orderBy('event_date')->get(); foreach ($events as $event) { /* … */ } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  40. Many to many relationships Reverse is the same as definition

    for User. class Event extends Model { public function users() { return $this->belongsToMany('App\User'); } } // usage: $users = $e->users()->where('name', 'like', 'R%')->get(); foreach ($users as $user) { /* … */ } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  41. Migrations Store schema changes in code Create a migration: $

    php artisan make:migration create_users_table Created Migration: 2018_03_31_143419_create_users_table Run migrations: $ php artisan migrate Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  42. CreateUsersTable up(): The changes you want to make class CreateUsersTable

    extends Migration { public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  43. Schema building Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); $table->string('username',

    50); $table->string('name', 100); $table->date('date_of_birth')->nullable(); $table->boolean('active')->default(true); $table->unique('username'); }); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  44. Seeding Seed you database with test data Create a seeder:

    $ php artisan make:seeder UsersTableSeeder Run seeds: $ php artisan db:seed Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  45. UsersTableSeeder class UsersTableSeeder extends Seeder { public function run() {

    DB::table('users')->insert([ ['username' => 'akrabat', 'name' => 'Rob'], ['username' => 'geeh', 'name' => 'Gary'], ]); } } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  46. Forms • Write your form in HTML • Validate in

    a separate action controller • Supports Post-Redirect-Get pattern out of the box Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  47. Set up routes One route for the form, one for

    processing it: // routes/web.php: Route::get('/user/edit/{id}', 'UserController@edit') ->name('user-edit'); Route::post('/user/doedit/{id}', 'UserController@doEdit') ->name('user-doedit'); Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  48. A form On the /profile/edit/{id} page: <h1>Update your profile</h1> <form

    action="{{ route('user-doedit', ['id' => $id]) }}" method="POST"> <label for="name">Name</label> <input name="name" value="{{ old('name', $user->name) }}"> <button>Update</button> </form> Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  49. Validate request data class UserController extends Controller { public function

    doEdit(Request $request) { $data = $request->validate([ 'name' => 'required|max:100', ]); // The data is valid - save it return redirect()->route('user-profile'); } } Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  50. $request->validate() • Validates the POSTed data against the rules array

    • Returns the data on success • Redirects back to the previous URL on failure (our form) Rules: • Format: 'fieldname' => 'rules|separated|by|pipes' • There's lots of validation rules; Look them up! Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  51. Display errors • The $errors variable is available in your

    template • Use $errors->any() to test if you have any errors • Access errors: • Collection: $errors->all() • Individual: $errors->get('name') Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  52. Authentication • Provided for you out-of-the-box: • App\User model •

    Controllers for registration, login, forgot password & reset password • View templates in resources/views/auth • Database migrations for users & password_resets tables • Enable: $ php artisan make:auth $ php artisan migrate Rob Allen ~ @akrabat | Gary Hockin ~ @geeh
  53. Operations Do we have a logged in user? $isLoggedIn =

    Auth::check(); Retrieve the logged in user: $user = Auth::user(); // or in a controller action: $user = $request->user(); Protect routes: Add the auth middleware to a route to restrict to logged in users Rob Allen ~ @akrabat | Gary Hockin ~ @geeh