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

I Didn't Know Laravel Could Do That!

I Didn't Know Laravel Could Do That!

If you’re into to Laravel, you’ve probably already tried out the basic MVC features. What you probably haven’t gotten to yet is all of the amazing features that make Laravel truly a “full stack” framework. In this talk, we’ll cover queueing jobs, send emails, search, browser tests, event broadcasting and more. This will be a great crash course on letting Laravel do the heavy lifting for you, and making sure you know what it’s capable of so you don’t accidentally reinvent the wheel.

Josh Butts

April 20, 2018
Tweet

More Decks by Josh Butts

Other Decks in Technology

Transcript

  1. I Didn’t Know Laravel
    Could Do That!
    Josh Butts
    Longhorn PHP 2018

    View full-size slide

  2. About Me
    • SVP of Engineering,

    Ziff Davis
    • Austin PHP Organizer
    • github.com/jimbojsb
    • @jimbojsb
    2

    View full-size slide

  3. Preface
    • I really used to dislike Laravel
    • I used to thing everyone had a smug sense
    of superiority
    • I used to think the only way to write PHP
    was explicitly
    • I’ll admit it, I was wrong
    3

    View full-size slide

  4. Agenda
    • Job & Queues
    • Email
    • Scout
    • Dusk
    • Echo
    4

    View full-size slide

  5. Jobs & Queues

    View full-size slide

  6. Laravel Queues - Why?
    • Offload slow tasks to asynchronous back-
    end processes
    • Keep web response time snappy
    • Especially for integrations with remote
    services
    • Things that have complex failure models
    6

    View full-size slide

  7. Laravel Queues
    • Built in to the framework
    • Various drivers for the queue service of
    your choice
    • Many other first-class parts of the
    framework are natively queue-able
    7

    View full-size slide

  8. Configuring Queues
    • Set the queue driver of your choice in
    your .env file (you don’t want sync)
    • Use artisan to make a failed jobs table
    (optional)
    • If using DB driver, use artisan to make a jobs
    table
    • Set up credentials for whatever queue
    service you need in config/queue.php or .env
    8

    View full-size slide

  9. DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret
    BROADCAST_DRIVER=log
    CACHE_DRIVER=file
    SESSION_DRIVER=file
    SESSION_LIFETIME=120
    QUEUE_DRIVER=sync
    REDIS_HOST=127.0.0.1
    REDIS_PASSWORD=null
    REDIS_PORT=6379

    View full-size slide

  10. For the love of God,
    please don’t use your
    database as a job queue

    View full-size slide

  11. Anatomy of a Job

    View full-size slide

  12. php artisan make:job MessageLoggerJob

    View full-size slide

  13. namespace App\Jobs;
    use Illuminate\Bus\Queueable;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Bus\Dispatchable;
    class MessageLoggerJob implements ShouldQueue
    {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    /**
    * Create a new job instance.
    *
    * @return void
    */
    public function __construct($message)
    {
    }
    /**
    * Execute the job.
    *
    * @return void
    */
    public function handle()
    {
    }
    }

    View full-size slide

  14. use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Bus\Dispatchable;
    class MessageLoggerJob implements ShouldQueue
    {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    private $message;
    public $tries = 5;
    public $timeout = 600;
    public $delay = 5;
    /**
    * Create a new job instance.
    *
    * @return void
    */
    public function __construct($message)
    {
    $this->message = $message;
    }
    /**
    * Execute the job.
    *
    * @return void
    */
    public function handle()
    {
    logger()->info($this->message);
    }
    }

    View full-size slide

  15. Dispatching Jobs

    View full-size slide

  16. namespace App\Http\Controllers;
    use App\Jobs\MessageLoggerJob;
    use Illuminate\Http\Request;
    class JobDispatcherController extends Controller
    {
    public function index(Request $request)
    {
    $job = new MessageLoggerJob($request->get('message'));
    dispatch($job);
    }
    }

    View full-size slide

  17. Processing Jobs

    View full-size slide

  18. php artisan queue:work

    View full-size slide

  19. php artisan queue:work --once

    View full-size slide

  20. Laravel Horizon
    • Fancy dashboard to manage all your
    queues
    • Has baked in assumptions about your
    deployment infrastructure
    • Manages supervisors for queue workers
    21

    View full-size slide

  21. Laravel Horizon
    22

    View full-size slide

  22. Sending Emails
    • Uses SwiftMailer under the hood
    • Create objects to represent emails
    • Email objects are natively queue-able
    • Email bodies are rendered using blade
    • Emails are natively aware of Laravel users
    for name and email address
    24

    View full-size slide

  23. Anatomy of an
    Email

    View full-size slide

  24. php artisan make:mail WelcomeEmail

    View full-size slide

  25. use Illuminate\Mail\Mailable;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Contracts\Queue\ShouldQueue;
    class WelcomeEmail extends Mailable
    {
    use Queueable, SerializesModels;
    public function __construct()
    {
    //
    }
    public function build()
    {
    return $this->view('view.name');
    }
    }

    View full-size slide

  26. class WelcomeEmail extends Mailable
    {
    use Queueable, SerializesModels;
    private $user;
    public function __construct(User $user)
    {
    $this->user = $user;
    }
    public function build()
    {
    return $this->view('welcome.blade.php');
    }
    }

    View full-size slide

  27. class WelcomeEmail extends Mailable implements ShouldQueue
    {
    use Queueable, SerializesModels;
    private $user;
    public function __construct(User $user)
    {
    $this->user = $user;
    }
    public function build()
    {
    $this->subject("Welcome to Our App");
    $this->from("[email protected]");
    return $this->view('welcome.blade.php');
    }
    }

    View full-size slide



  28. Welcome to our app, {{$user->name}}
    Please confirm your email,
    click here



    View full-size slide

  29. Sending Emails

    View full-size slide

  30. namespace App\Http\Controllers;
    use App\Mail\WelcomeEmail;
    use App\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Mail;
    class MailSenderController extends Controller
    {
    public function index(User $user)
    {
    $mail = new WelcomeEmail($user);
    Mail::to($user)->send($mail);
    }
    }

    View full-size slide

  31. namespace App\Http\Controllers;
    use App\Mail\WelcomeEmail;
    use App\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Mail;
    class MailSenderController extends Controller
    {
    public function index(User $user)
    {
    $mail = new WelcomeEmail($user);
    Mail::to($user)->queue($mail);
    }
    }

    View full-size slide

  32. Nobody ever wants to build search
    • Good news, its basically free in Laravel
    • Well, free as free time, but not necessarily
    free as in beer
    • Works best with Algolia, which is totally
    worth whatever you pay them
    35

    View full-size slide

  33. Install Laravel
    Scout

    View full-size slide

  34. composer require laravel/scout
    php artisan vendor:publish

    View full-size slide

  35. A word on Algolia

    View full-size slide

  36. 'algolia' => [
    'id' => env('ALGOLIA_APP_ID', ''),
    'secret' => env('ALGOLIA_SECRET', ''),
    ],

    View full-size slide

  37. Make your models
    searchable

    View full-size slide

  38. namespace App;
    use Illuminate\Database\Eloquent\Model;
    use Laravel\Scout\Searchable;
    class BlogPost extends Model
    {
    use Searchable;
    }

    View full-size slide

  39. Index all the
    things

    View full-size slide

  40. php artisan scout:import "App\BlogPost"

    View full-size slide

  41. BlogPost::all()->searchable();

    View full-size slide

  42. Search for things

    View full-size slide

  43. BlogPost::search("release notes")->get();

    View full-size slide

  44. Drawbacks
    • Scout is EASY
    • There are tradeoffs
    • Scout hides the true power of Algolia and
    other engines
    47

    View full-size slide

  45. Browser Tests

    View full-size slide

  46. Wait I already knew this one
    • Laravel comes with really nice HTTP
    dispatching tests built in
    • These won’t be sufficient for SPAs or apps
    that have Vue or React components
    49

    View full-size slide

  47. Laravel Dusk

    View full-size slide

  48. composer require --dev laravel/dusk

    View full-size slide

  49. Writing Dusk
    Tests

    View full-size slide

  50. php artisan dusk:make MyHomepageTest

    View full-size slide

  51. namespace Tests\Browser;
    use Tests\DuskTestCase;
    use Laravel\Dusk\Browser;
    use Illuminate\Foundation\Testing\DatabaseMigrations;
    class MyHomepageTest extends DuskTestCase
    {
    public function testExample()
    {
    $this->browse(function (Browser $browser) {
    $browser->visit('/')
    ->assertSee('Laravel');
    });
    }
    }

    View full-size slide

  52. use Laravel\Dusk\Browser;
    use Illuminate\Foundation\Testing\DatabaseMigrations;
    class MyHomepageTest extends DuskTestCase
    {
    /**
    * A Dusk test example.
    *
    * @return void
    */
    public function testExample()
    {
    $this->browse(function (Browser $browser) {
    $browser->visit('/')
    ->drag('#available', '#selected')
    ->assertSee('Success!');
    });
    }
    }

    View full-size slide

  53. Running Dusk
    Tests

    View full-size slide

  54. php artisan dusk

    View full-size slide

  55. OMG
    SCREENSHOTS

    View full-size slide

  56. Events
    • Laravel ships with a full-featured event bus
    • Create event objects
    • Dispatch events
    • Listen for events
    • Queue events
    • Broadcast events
    61

    View full-size slide

  57. Creating Events

    View full-size slide

  58. php artisan make:event SignupEvent

    View full-size slide

  59. use Illuminate\Foundation\Events\Dispatchable;
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    class SignupEvent
    {
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public function __construct()
    {
    //
    }
    public function broadcastOn()
    {
    return new PrivateChannel('channel-name');
    }
    }

    View full-size slide

  60. use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    class SignupEvent
    {
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $user;
    public $request;
    public function __construct(User $user, Request $request)
    {
    $this->user = $user;
    $this->request = $request;
    }
    }

    View full-size slide

  61. Dispatching
    Events

    View full-size slide

  62. namespace App\Http\Controllers;
    use App\Events\SignupEvent;
    use App\User;
    use Illuminate\Http\Request;
    class SignupController extends Controller
    {
    public function register(Request $request)
    {
    $user = new User($request->all());
    $user->save();
    $event = new SignupEvent($user, $request);
    event($event);
    }
    }

    View full-size slide

  63. Listening for
    Events

    View full-size slide

  64. php artisan make:listener SignupListener

    View full-size slide

  65. namespace App\Listeners;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    class SignupListener
    {
    public function __construct()
    {
    //
    }
    public function handle($event)
    {
    //
    }
    }

    View full-size slide

  66. namespace App\Listeners;
    use App\Mail\WelcomeEmail;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Support\Facades\Mail;
    class SignupListener
    {
    public function handle($event)
    {
    $mail = new WelcomeEmail($event->user);
    Mail::to($event->user)->send($mail);
    }
    }

    View full-size slide

  67. Broadcasting Events
    • Laravel can sent your events real-time to
    the client using Web Sockets
    • Laravel Echo JS lib
    • Laravel doesn’t natively have a socket
    server, and PHP is terrible for this
    • Native Pusher, Socket.io support
    72

    View full-size slide

  68. Broadcasting
    Events

    View full-size slide

  69. class FollowedEvent implements ShouldBroadcast
    {
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $user;
    public $followedBy;
    public function __construct(User $user, User $followedBy)
    {
    $this->user = $user;
    $this->followedBy = $followedBy;
    }
    public function broadcastOn()
    {
    return new PrivateChannel($this->user->id);
    }
    }

    View full-size slide

  70. Dispatching
    Broadcasts

    View full-size slide

  71. Dispatching Broadcasts…
    is exactly like dispatching
    other events.

    View full-size slide

  72. Receiving Events
    in the Browser

    View full-size slide

  73. npm install --save laravel-echo

    View full-size slide

  74. Find a Web Socket
    Server

    View full-size slide

  75. window.Echo = new Echo({
    broadcaster: 'pusher',
    key: ‘…’,
    cluster: 'us',
    encrypted: true
    });
    Echo.private(user.id)
    .listen('Followed', function(e) {
    // maybe append a flash-messagy-sorta-thing?
    });

    View full-size slide

  76. Questions?
    https://joind.in/talk/427b0

    View full-size slide

  77. We’re Hiring - NYC, Montreal
    82

    View full-size slide