5.3 feature announcement notes from Laracon
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:
- Install Passport via Composer.
- Go to
config/app
, addLaravel\Passport\PassportServiceProvider
to your providers list. - Run new migrations using 5.3's multiple migrations paths. Just run
php artisan migrate
and it'll include the Passport migrations too. - Go to your user and import trait
Laravel\Passport\HasApiTokens
- Add routes: go to
AuthServiceProvider
and useLaravel\Passport\Passport
, then in theboot()
method runPassport::routes()
- (optionally) Add scopes in the
boot()
method ofAuthServiceProvider
, afterPassport::routes()
; e.g.Passport::tokensCan(['conference' => 'Access your conference information'])
config/auth.php
,guards.api.driver
; change theapi
guard to usepassport
driver instead oftoken
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:
Creating a client:
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:
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:
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:
-
Jun 16, 2016 | laravel, laravel 5.3, echo, websockets
-
Jun 27, 2016 | laravel, laravel 5.3
-
Jun 29, 2016 | laravel, laravel 5.3, eloquent
-
Jul 6, 2016 | laravel, laravel 5.3
-
Jul 8, 2016 | laravel, laravel 5.3
-
Jul 8, 2016 | laravel, laravel 5.3, eloquent
-
Jul 25, 2016 | laravel, laravel 5.3
-
Jul 26, 2016 | laravel, laravel 5.3
-
Jul 27, 2016 | laravel, laravel 5.3, routing
-
Jul 27, 2016 | laravel, laravel 5.3, laravel scout, laravel passport, mailable
-
Jul 29, 2016 | laravel, laravel 5.3, laravel scout
-
Jul 30, 2016 | laravel, laravel 5.3, laravel passport, oauth
-
Aug 5, 2016 | laravel, laravel 5.3, mail, laravel mailables
-
Aug 8, 2016 | laravel, laravel 5.3
-
Oct 19, 2016 | laravel, laravel 5.3, laravel notifications, notifications
-
Dec 21, 2016 | laravel, laravel 5.3, vuejs, vueify, authorization
-
Dec 21, 2016 | laravel, laravel 5.3, queues
-
Jan 30, 2017 | laravel, laravel 5.3, artisan