Jul 27, 2016 | laravel, laravel 5.3, laravel scout, laravel passport, mailable

5.3 feature announcement notes from Laracon

Series

This is a series of posts on New Features in Laravel 5.3.

!
Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

Once the videos are released I'll be able to update this post with more info, since I was on an audio-only stream for the majority of this.


I'll be writing my usual longer, in-depth blog posts about each of the new 5.3 features that are releasing during Taylor's Laracon talk today, but I wanted to find a single place to write down my notes about the new features that Taylor is announcing for the first time today, so I figured, why not put it in a single blog post here?

This is just my notes from the live stream. I'll update this later with more info, and then will write full-length posts; this will just be casual notes.


Laravel Scout

Intro to Scout

Search/ElasticSearch driver; packaged separately like Cashier. Works best with Algolia but would love community support for other drivers.

Model is going to have a Searchable trait.

Indexing with Scout

Indexes the toArray() function on the model and puts it up in the search index.

Add ScoutServiceProvider to config/app and Searchable trait to your model.

The trait hooks into Eloquent events. Listens to those events and updates your indexes in response.

Special overrides/etc.

Closure that allows you to override indexing:

Post::withoutSyncingToSearch(function () {
    // make a bunch of posts, e.g.
    factory(Post::class, 10)->create();
});

... then later update all of those:

// Could just scope down the query to only those which you haven't indexed yet if you want...
Post::all()->searchable();

Also could do: this on a relationship

$user->posts()->searchable();

It's smart enough to be like "upsert"; it updates any that are already there, and inserts any new ones.

Can also remove from search:

// didn't catch the syntax for this one, sorry! probably something like Post::where('a', 'b')->unsearchable();

Queues/etc.

These interactions feel slow—makes sense; these are HTTP requests going out!

So: in config/scout.php set queue to true so that these updates are set to be synced async.

// not sure what this does or whether i wrote this syntax down right, feed was cutting out
php artisan scout:import App\Post

Searching with Scout

You can seearch.. something like:

Post::search('Alice')->get();
Post::search('Alice')->paginate(20);
Post::search('Alice')->where('account_id', 2)->get();

It can't do the full range of SQL where clauses, but it handles the basics.

Mailables

Want to simplify mail, so creating mail objects:

Mail::to($user)->send(new DeploymentCompleted($server));

DeploymentCompleted is a PHP class; it represents an email.

// mailable class
public function construct($server)
{
    $this->server = $server;
}

public function build()
{
    return $this->view('emails.whatever.viewname');

    // second parameter is an optional array of specific data that you want to be available to view:

    return $this->view('emails.whatever', ['explicit_data_passed' => 'abc']);
}

Any public properties on the mailable object are accessible in the view, so you don't have to explicitly pass any data.

Send to multiple

Mail::to(User::find(1))
    ->cc(User::find(2))
    ->bcc(User::find(3))
    ->send(new etc.);

Mail::to(Users::all())
    ->send(new etc.);

Queueing mailables

Mail::to($user)->queue(new etc.);

Attachments

All the same methods you have within your mail closure like attach.

public function build()
{
    return this->view()->subject()->attach();
}

Misc

Guesses subject from the class name if you don't set it explicitly. E.g. mailable class "DeploymentCompleted" gets auto subject "Deployment Completed".

Laravel's new Notification features

Intro to Notification

Quick notifications. Password resets, quick links, etc.

Limited features. No file attachments, CCs, etc. This is not email.

Password reminder in 5.3 will use this out of the box.

$user->notify(new DeploymentCompleted($server));

Basic notification class

class DeploymentCompleted
{
    public function construct($server)
    {
        $this->server = $server;
    }

    public frunction via($notifiable)
    {
        // $notifiable might be a user.. but who knows, you might want to notify a server or a slack channel or something
        // you could inspect user preferences here to decide which sort of notification they get
        // return a list of notification "drivers"
        return ['mail'];
    }

    public function message()
    {
        $this->line('You have a new deployment!')
            ->action('View Deployment', 'http://laravel.com')
            ->line('Check it out');
    }
}

Notifiable trait.

Mail driver comes with a slick default template, responsive, etc. but you can also export/publish it into your app and customize it yourself.

Different states:

$this->line()->action()->line()->error();

Differentiating notification states

Some drivers know how to differntiate states; some don't. For example, the error state in the mail driver will get a big red button instead of a big blue button. The success() state gets a green button.

New settings in config/app.php: name and logo for notifications.

Email driver

Covered above.

Database driver

Table that holds these notifications. Polymorphic; columns for notifiable type, id, level, intro, outro, action text, action url, has been read or not. Laravel doesn't know how to check whether it's read or not, you handle that.

Just add 'database' to the via() method and all of a sudden it's getting it; your calling code doesn't know or care which via driver it's gonna use.

Slack driver

Add slack to the via() method. Some drivers require more info. Context:

routeNotificationForSlack() method on the User (or whatever else is notifiable).

Convention is routeNotificationFor{drivernNameHere}.

For Slack, that method should return Slack webhook URL: e.g. return $this->slack_webhook_url.

SMS driver

Add nexmo or sms, hard to tell from Taylor's audio. Add your Nexmo api keys

Queueing notifications

Go to the notification class and add the ShouldQueue trait. Now they're all queued. Boom goes the dynamite.

Laravel Passport

Full OAuth2 Server implementation in Laravel in like 5 minutes!!!!!!!!!

In Laravel 5.2, we got A) the idea of multiple auth drivers and B) the token-based authentication. Token-based auth works, it's fine, but it's more important as the ground layer for this.

Basic installation of Passport

Steps to use it:

  1. Install Passport via Composer.
  2. Go to config/app, add Laravel\Passport\PassportServiceProvider to your providers list.
  3. Run new migrations using 5.3's multiple migrations paths. Just run php artisan migrate and it'll include the Passport migrations too.
  4. Go to your user and import trait Laravel\Passport\HasApiTokens
  5. Add routes: go to AuthServiceProvider and use Laravel\Passport\Passport, then in the boot() method run Passport::routes()
  6. (optionally) Add scopes in the boot() method of AuthServiceProvider, after Passport::routes(); e.g. Passport::tokensCan(['conference' => 'Access your conference information'])
  7. config/auth.php, guards.api.driver; change the api guard to use passport driver instead of token

Passport frontend

Passport exposes a JSON API for your frontend to consume to let you manage it.

Comes with Vue components by default to make it easy for you to manage them, if you want to use them. It's just a reference but you could use if you want.

Three default Vue components out of the box:

<!-- let people make clients -->
<passport-clients></passport-clients>

<!-- list of clients people have authorized to access our account -->
<passport-authorized-clients></passport-authorized-clients>

<!-- make it simple to generate a token right in the UI to play with -->
<passport-personal-access-tokens></passport-personal-access-tokens>

For these examples Taylor made an app at http://passport.dev/ that has Passport installed. This app is providing the OAuth API. Then another at http://example.dev/ that is a client, consuming it.

Look by default:

Screenshot of Laravel Passport default components view

Creating a client:

Screenshot of Laravel Passport create client view

Once you create a client, you get a secret and a client ID. Go to your consuming client (another site, etc.) and put that key and ID in there.

Showed a sample app that CONSUMES this API (not that provides it); lives at http://consumer.dev/:

// routes/web.php

use Illuminate\Http\Request;

Route::get('/', function () {
    $query = http_build_query([
        'client_id' => 1,
        'redirect_uri' => 'http://consumer.dev/callback',
        'response_type' => 'code',
        'scope' => 'conference'
    ]);

    return redirect('http://passport.dev/oauth/authorize?' . $query);
});

Route::get('callback', function (Request $request) {
    $http = new GuzzleHttp\Client;

    $response = $http->post('http://passport.dev/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization-code',
            'client_id' => 1, // from admin panel above
            'client_secret' => 'abc', // from admin panel above
            'redirect_uri' => 'http://consumer.dev/callback',
            'code' => $request->code
        ]
    ]);

    return json_decode((string) $response->getBody(), true)['access_token'];
});

When you visit http://consumer.dev/ it tries to authenticate, sending you over the Passport site; you get this screen:

Screenshot of Laravel Passport auth approval view

When you authorize, takes you back to http://consumer.dev/callback and you have access to your token now.

To prove, Taylor makes a route in his passport app that just returns the authenticated user, puts it in the routes/api.php routes file. Calls it from Postman, pastes the JWT token from above into the Authorization header and calls the page, and it just works. (Authorization: Bearer TOKENHERE)

Easy to revoke applications in the UI:

Screenshot of Laravel Passport revoke token view

Keeps saying: "This is not what your UI needs to look like; it's just a free reference application."

New in Passport that League package doesn't have: Want it to be easy to create a token in the UI to just play around with the API. Since every token is associated with a client (last one we made was associated with http://consumer.dev), make a personal client: php artisan make passport:client --personal. Then you can go to the Personal Access Tokens component and hit "Create New Token". Creates them with your app (passport.dev) as the listed client.

Scope middleware

Middlewares to limit users' route access based on your scopes. Add them (whatever they are) in the HTTP kernel. scope and scopes.

The scope middleware authenticates you for a single scope; scopes requires all defined scopes.

// add to Http\Kernel $route<iddleware property
// you can name it whatever you want
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,

If you want to limit the user to only access a route if they have the conference scope:

Route::get('/whatever', function () {
    // do stuff
})->middleware('scope:conference');

Multiples can be comma separated; allows user through if they have any of the provided scopes: ->middleware('scope:conference,otherScope'). If you want it to only let them through if they have all passed scopes, use scopes:->middleware('scopes:conference,otherScope')`.

Super-powered access to the API for frontend views

If you have a frontend that's consuming the API, you may not want to do the whole OAuth dance. But you might want the OAuth flow to still be available for external API users.

Trick for your frontend--which has your user already authenticated via Laravel and sessions--to access your API and get around the OAuth flow.

Go to HTTP\Kernel and add new middleware to web:

`Laravel\Passport\Http\Middleware\CreateFreshApiToken::class`,

This adds a JWT token as a cookie to anyone who's logged in. Uses "Synchronized token pattern" to embed the CSRF token into the JWT, and require a CSRF header if you sent that cookie, and they have to match. Some kinda magic.

Safe because other apps can't read your cookies so they can't get your CSRF token out of the JWT token. Boom. Can make API requests if logged in without worrying about OAuth tokens.

Miscellaneous

"My API doesn't have to be an after thought." Set the whole thing up in 15 minutes with demos.

Latest League package so they're JWT tokens.


Comments? I'm @stauffermatt on Twitter


Tags: laravel  •  laravel 5.3  •  laravel scout  •  laravel passport  •  mailable


This is part of a series of posts on New Features in Laravel 5.3:

  1. Jun 16, 2016 | laravel, laravel 5.3, echo, websockets
  2. Jun 27, 2016 | laravel, laravel 5.3
  3. Jun 29, 2016 | laravel, laravel 5.3, eloquent
  4. Jul 6, 2016 | laravel, laravel 5.3
  5. Jul 8, 2016 | laravel, laravel 5.3, eloquent
  6. Jul 25, 2016 | laravel, laravel 5.3
  7. Jul 26, 2016 | laravel, laravel 5.3
  8. Jul 27, 2016 | laravel, laravel 5.3, routing
  9. Jul 27, 2016 | laravel, laravel 5.3, laravel scout, laravel passport, mailable
  10. Jul 29, 2016 | laravel, laravel 5.3, laravel scout
  11. Jul 30, 2016 | laravel, laravel 5.3, laravel passport, oauth
  12. Aug 5, 2016 | laravel, laravel 5.3, mail, laravel mailables
  13. Aug 8, 2016 | laravel, laravel 5.3
  14. Oct 19, 2016 | laravel, laravel 5.3, laravel notifications, notifications
  15. Dec 21, 2016 | laravel, laravel 5.3, vuejs, vueify, authorization
  16. Dec 21, 2016 | laravel, laravel 5.3, queues
  17. Jan 30, 2017 | laravel, laravel 5.3, artisan

Subscribe

For quick links to fresh content, and for more thoughts that don't make it to the blog.