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

Leveraging Laravel: Launching Side Projects Quickly With Laravel

Leveraging Laravel: Launching Side Projects Quickly With Laravel

My talk from Laracon US 2015, Sunshine PHP 2016, and Nomad PHP February 2016

http://giscus.co/

http://github.com/tightenco/giscus

Matt Stauffer

August 11, 2015
Tweet

More Decks by Matt Stauffer

Other Decks in Technology

Transcript

  1. LEVERAGING LARAVEL @STAUFFERMATT RAD IS RAPID APPLICATION DEVELOPMENT Contrast to

    waterfall development Tied to Agile & other management/development practices Adjusting requirements as more knowledge gained In short: Develop applications… rapidly… then iterate
  2. LEVERAGING LARAVEL @STAUFFERMATT WORKING FOR BIG AGENCIES TAUGHT ME HOW

    TO PRIORITIZE MY TIME AND TRIAGE MY REMAINING WORK
  3. LEVERAGING LARAVEL @STAUFFERMATT LARAVEL’S RAD SOUL MEANS: Get the repetitive

    cruft out of the way so you can make awesome apps,
 fast. LEVERAGING LARAVEL @STAUFFERMATT
  4. LEVERAGING LARAVEL @STAUFFERMATT MOVING QUICKLY FROM IDEA TO MVP:
 THREE

    APPROACHES LEVERAGING LARAVEL @STAUFFERMATT 1. Build the backend first with simple CRUD views
 (Create, Read, Update, Delete) 2. Build frontend views with no/stubbed data 3. Build individual features all through, one at a time
  5. LEVERAGING LARAVEL @STAUFFERMATT JTBD-LIGHT WHEN BUILDING SIDE PROJECTS Identify minimum

    functionality required to do this job (like TDD),
 then build that out frontend-to-backend; launch, gather feedback, repeat
  6. LEVERAGING LARAVEL @STAUFFERMATT *(ONE MORE REASON OUR IDEAS DON’T BECOME

    REALITY: Moving from a general idea of how a technology works to understanding how to concretely implement it right now can often eat up all the time on your side projects)
  7. LEVERAGING LARAVEL @STAUFFERMATT “I want a tool that notifies me

    when someone comments on one of my Gists”
  8. LEVERAGING LARAVEL @STAUFFERMATT OK, WHAT SCREENS WILL WE NEED? Landing

    (sales) page User signup (enters payment information and agrees to pay $3/mo) User dashboard
  9. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  10. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge DEVELOPMENT ENVIRONMENT BOOTSTRAP APPLICATION WRITE SPECIFIC FUNCTIONALITY PAYMENTS DEPLOY
  11. LEVERAGING LARAVEL @STAUFFERMATT { SAME ACROSS MOST PROJECTS { SAME

    ACROSS MOST PROJECTS Develop with Homestead Create Laravel Application Use Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge
  12. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  13. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  14. LEVERAGING LARAVEL @STAUFFERMATT $ laravel new giscus ... some ...

    boring ... installation ... stuff Application ready! Build something amazing.
  15. LEVERAGING LARAVEL @STAUFFERMATT $ cd giscus $ cp .env.example .env

    $ php artisan key:generate $ php artisan serve
  16. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  17. LEVERAGING LARAVEL @STAUFFERMATT // gulpfile.js var elixir = require(‘laravel-elixir’); /*

    |-------------------------------------------------------------------------- | Elixir Asset Management |-------------------------------------------------------------------------- | | Elixir provides a clean, fluent API for defining some basic Gulp tasks | for your Laravel application. By default, we are compiling the Sass | file for our application, as well as publishing vendor resources. | */ elixir(function(mix) { mix.sass(‘app.scss’); });
  18. LEVERAGING LARAVEL @STAUFFERMATT // In resources/assets/sass/app.scss // Un-comment this, and

    boom, you’ve got Bootstrap: @import “node_modules/bootstrap-sass/assets/stylesheets/bootstrap”;
  19. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  20. LEVERAGING LARAVEL @STAUFFERMATT LEVERAGING LARAVEL @STAUFFERMATT What’s going to back

    this data? Eloquent User model Laravel Socialite GitHub (OAuth)
  21. LEVERAGING LARAVEL @STAUFFERMATT GITHUB AUTH WITH SOCIALITE LEVERAGING LARAVEL @STAUFFERMATT

    BASICS: Install Socialite with Composer Set up GitHub Application Paste OAuth Credentials into Laravel config file Set up a few routes for OAuth https://mattstauffer.co/blog/using-github- authentication-for-login-with-laravel-socialite
  22. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  23. LEVERAGING LARAVEL @STAUFFERMATT WHAT ARE OUR ROUTES? Home page GitHub

    auth (GitHub auth callback) Initial sign-up Dashboard Logout
  24. LEVERAGING LARAVEL @STAUFFERMATT LEVERAGING LARAVEL @STAUFFERMATT WHAT ARE OUR ROUTES?

    Home page GitHub auth (GitHub auth callback) Initial sign-up Dashboard Logout / /auth/github /auth/github/callback /sign-up /dashboard /logout
  25. LEVERAGING LARAVEL @STAUFFERMATT STUB CONTROLLERS AND VIEWS // app/Http/routes.php Route::get('/',

    'SalesController@index'); Route::get('auth/github', 'AuthController@github'); Route::post('auth/github/callback', 'AuthController@githubCallback'); Route::get('sign-up', 'SignupController@index'); Route::get('dashboard', 'DashboardController@index'); Route::get('logout', 'AuthController@logout');
  26. LEVERAGING LARAVEL @STAUFFERMATT STUB CONTROLLERS AND VIEWS <?php namespace App\Http\Controllers;

    class SalesController extends Controller { public function index() { return view('sales'); } }
  27. LEVERAGING LARAVEL @STAUFFERMATT STUB CONTROLLERS AND VIEWS <?php namespace App\Http\Controllers;

    class SignupController extends Controller { public function index() { return view(‘sign-up'); } }
  28. LEVERAGING LARAVEL @STAUFFERMATT STUB CONTROLLERS AND VIEWS <?php namespace App\Http\Controllers;

    class DashboardController extends Controller { public function index() { return view('dashboard'); } }
  29. LEVERAGING LARAVEL @STAUFFERMATT STUB CONTROLLERS AND VIEWS <?php namespace App\Http\Controllers;

    use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; class AuthController extends Controller { // ... lots of Socialite stuff public function logout() { Auth::logout(); return redirect('/'); } }
  30. LEVERAGING LARAVEL @STAUFFERMATT BUILD VIEWS: MASTER BLADE LAYOUT <!-- resources/views/layouts/app.blade.php

    --> <html> <head> <!-- etc. --> </head> <body> <div class="container"> <!-- header, nav, etc. --> <!-- Content here --> </div> <!-- Footer scripts here --> </body> </html>
  31. LEVERAGING LARAVEL @STAUFFERMATT BUILD VIEWS: MASTER BLADE LAYOUT <!-- resources/views/layouts/app.blade.php

    --> <html> <head> <!-- etc. --> </head> <body> <div class="container"> <!-- header, nav, etc. --> @yield('content') </div> @yield('footerScripts') </body> </html>
  32. LEVERAGING LARAVEL @STAUFFERMATT BUILD VIEWS: CHILD EXTENSIONS <!-- resources/views/home.blade.php -—>

    @extends('layouts.app') @section('content') Test @stop @section('footerScripts') <script> $(function() { /* do stuff */ }); </script> @stop
  33. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  34. LEVERAGING LARAVEL @STAUFFERMATT FIRST: WHAT SHOULD THE FEATURE OPERATE LIKE

    AT THE HIGHEST LEVEL? Cron running through all users For each user, get any new Gist comments since last cron Send notification email for each new comment
  35. LEVERAGING LARAVEL @STAUFFERMATT // Get all users from Eloquent User::all()->each(function

    ($user) { // Queue a command and pass in the user Queue::push(NotifyUserOfNewGistComments::class, [ ‘user’ => $user ]); });
  36. LEVERAGING LARAVEL @STAUFFERMATT ... class NotifyUserOfNewGistComments extends Job { ...

    public function fire($job, $data) { // Authorize as this user // Get all new gist comments for this user // Kick off a notification email for comment } }
  37. LEVERAGING LARAVEL @STAUFFERMATT ... class NotifyUserOfNewGistComments extends Job { ...

    public function fire($job, $data) { $this->client->authorizeAs($data['user']->stuff); foreach ($this->client->newGistComments() as $comment) { Queue::push(NotifyUserOfNewGistComment::class, [ 'user' => $data['user'], 'comment' => $comment ]); } } }
  38. LEVERAGING LARAVEL @STAUFFERMATT ... class NotifyUserOfNewGistComment extends Job { ...

    public function fire($job, $data) { // Send an email to $data[‘user’] with info about $data[‘comment’] } }
  39. LEVERAGING LARAVEL @STAUFFERMATT public function fire() { foreach ($this->client->api('gists')->all() as

    $gist) { foreach ($this->client->api('gist')->comments()->all($gist['id']) as $comment) { $this->handleComment($comment, $gist, $data['user']); } } }
  40. LEVERAGING LARAVEL @STAUFFERMATT public function handleComment($comment, $gist, $user) { if

    ($this->commentNeedsNotification($comment)) { $this->notifyComment($comment, $gist, $user); } }
  41. LEVERAGING LARAVEL @STAUFFERMATT public function commentNeedsNotification($comment) { return NotifiedComment::where('github_id', $comment['id'])

    ->where('updated_at', $comment['updated_at']) ->count() == 0; } public function notifyComment($comment, $gist, $user) { Queue::push(NotifyUserOfNewGistComment::class, [ 'user' => $user, 'comment' => $comment, 'gist' => $gist ]); }
  42. LEVERAGING LARAVEL @STAUFFERMATT ... class NotifyUserOfNewGistComment extends Job { ...

    public function fire($job, $data) { $this->sendNotificationEmail( $data[‘comment’], $data[‘gist’], $data[‘user’], ); $this->markCommentAsNotified($data[‘comment’]); } }
  43. LEVERAGING LARAVEL @STAUFFERMATT private function sendNotificationEmail($comment, $gist, $user) { Mail::send(

    'emails.new-comment', [ 'comment' => $comment, 'gist' => $gist, 'user' => $user ], function ($message) use ($user) { $message ->to($data['user']->email, $data['user']->name) ->subject('You have a new Gist Comment!'); } ); }
  44. LEVERAGING LARAVEL @STAUFFERMATT private function markCommentAsNotified($comment) { $eloquentComment = NotifiedComment::firstOrNew([

    'github_id' => $comment['id'] ]); $eloquentComment->github_updated_at = $comment['updated_at']; $eloquentComment->save(); }
  45. LEVERAGING LARAVEL @STAUFFERMATT <!-- resources/views/emails/new-comment.blade.php --> <p>You received a new

    comment on a Gist, <a href="{{ $gist['html_url'] }}">{{ $gist['description'] }}</a>.</p> <table><tr> <td><img src="{{ $comment['user']['avatar_url'] }}"></td> <td> <div> <a href="{{ $comment['user']['html_url'] }}"> {{ $comment['user']['login'] }} </a> commented <a href="{{ $gist['html_url'] }}#gistcomment-{{ $comment['id'] }}"> {{ $date->diffForHumans() }} </a> </div> {!! Markdown::parse($comment['body']) !!} </td> </tr></table>
  46. LEVERAGING LARAVEL @STAUFFERMATT // app/Console/Kernel.php $schedule->call(function () { User::all()->each(function ($user)

    { Queue::push(NotifyUserOfNewGistComments::class, [ 'user' => $user ]); }); })->hourly();
  47. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  48. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER 2) INSTALL CASHIER

    Then add to service providers array in $ composer require laravel/cashier:~5.0 Laravel\Cashier\CashierServiceProvider::class config/app.php
  49. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER $ php artisan

    cashier:table users $ php artisan migrate 3) ADD CASHIER FIELDS TO USERS TABLE
  50. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER use Laravel\Cashier\Billable; use

    Laravel\Cashier\Contracts\Billable as BillableContract; class User extends Model implements BillableContract { use Billable; protected $dates = ['trial_ends_at', 'subscription_ends_at']; ... 4) ADD CASHIER TRAITS TO USER MODEL
  51. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER // services.php 'stripe'

    => [ 'model' => App\User::class, 'key' => env('STRIPE_API_KEY'), 'secret' => env('STRIPE_API_SECRET'), ], 5) SET STRIPE API KEY & SECRET
  52. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER <form action="" method="POST">

    <script src="https://checkout.stripe.com/checkout.js" class="stripe-button" data-key="pk_test_n6DGsasdoifjpqowerjPOERW" data-amount="300" data-name="Giscus" data-description="Monthly Subscription ($3/mo)" data-image="/128x128.png"> </script> </form> 6) COPY STRIPE EMBEDDED HTML FROM DOCS INTO YOUR APP
  53. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER Route::get('sign-up', function ()

    { return view('sign-up-form-from-stripe'); }); Route::post('sign-up', function () { $token = Input::get('stripeToken'); Auth::user()->subscription('monthly')->create($token); return 'Subscribed!'; }); 7) CREATE ROUTES FOR THE STRIPE CREDIT CARD FORM
  54. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER 8) ADD A

    SUBSCRIBED MIDDLEWARE then edit: $ php artisan make:middleware Subscribed public function handle($request, $next) { if (! Auth::user()->subscribed()) { return redirect('sign-up'); } return $next->$request; }
  55. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER 8) ADD A

    SUBSCRIBED MIDDLEWARE (CONTINUED) Then add to app/Http/kernel.php as a route middleware
  56. LEVERAGING LARAVEL @STAUFFERMATT LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER

    9) ADD A ROUTE FOR YOUR CANCEL BUTTON Route::get('user/cancel', function() { Auth::user()->subscription()->cancel(); return redirect('/'); });
  57. LEVERAGING LARAVEL @STAUFFERMATT TAKING PAYMENTS WITH CASHIER 10) SHOW INVOICES

    // In a view somewhere… @foreach (Auth::user()->invoices() as $invoice) <li>{{ $invoice->dateString() }} | {{ $invoice->dollars() }} | <a href="/user/invoice/{{ $invoice->id }}">Download</a></li> @endforeach // routes.php… Route::get('user/invoice/{invoice}', function ($invoiceId) { return Auth::user()->downloadInvoice($invoiceId, [ 'vendor' => 'Tighten Co.', 'product' => 'Giscus', ]); });
  58. LEVERAGING LARAVEL @STAUFFERMATT Develop with Homestead Create Laravel Application Use

    Elixir & Bootstrap Configure Users & Socialite Stub Routes, Controllers, and Views Make the core functionality Take Payments with Cashier Deploy with Forge PROCESS
  59. LEVERAGING LARAVEL @STAUFFERMATT Set up email (Mandrill, Mailgun, etc.) and

    other services to function correctly from your live site 8) CONNECT TO THIRD-PARTY SERVICES
  60. LEVERAGING LARAVEL @STAUFFERMATT YOU HAVE GREAT IDEAS. ELIMINATE BARRIERS TO

    SHIPPING. SHIP EARLY, GET FEEDBACK, ITERATE. PICK ONE FEATURE; WRITE THE API; MAKE IT FUNCTION. Takeaways