-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[9.x] Introduce Laravel Precognition #44339
Conversation
1344e2e
to
3f6982c
Compare
5ea0d3d
to
9652e72
Compare
Documentation: laravel/docs#8261 |
Super exciting feature! |
This is epic, thanks for pushing leaps forward with implementations like this! Will be so useful for my business. So normal rate limits for requests will still apply here? |
@timacdonald nice one as always. I think this can be at 10.x instead of 9.x |
I just wanted to let you know that the description of this PR is out of this world. Good job, I am really excited for this feature. 💯 |
Top notch! |
Hey all, I sent in a PR to the skeleton to turn this on by default: laravel/laravel#5997. Would love to have some feedback there 👍 |
Thank you @timacdonald ! |
closes #77 As the container parameter is nullable in `Illuminate\Routing\Router`'s constructor, an empty container is assigned in its constructor when no container is provided. Since laravel/framework#44339 introduced `Illuminate/Routing/Contracts/CallableDispatcher`, a router needs this interface bound to dispatch closure bound routes such as the ones registered here: https://github.com/spatie/laravel-missing-page-redirector/blob/ba2bc5f2e9cf3be883c311c125c756903eae412d/src/MissingPageRouter.php#L42-L53 This PR - Passes Laravel Container to the `Router` constructor in this package's Service Provider, so it has a container which knows how to build a `CallableDispatcher` instance.
Is there any documentation for this yet? Can it be used with Livewire and Alpine for real-time form validation? 🤔 |
The documentation is here, it is not yet usable |
You don't need this to validate in real-time with Livewire |
Just read this feature on Laravel News May 17. Found the NPM package for Vue (https://www.npmjs.com/package/laravel-precognition-vue), But the GitHub link is gone. The documentation link (https://laravel.com/docs/precognition) is error 404. |
@christhofer docs for precognition are not merged yet. You can review the pull request for docs, but that can't be considered final: laravel/docs#8261 I guess Precognition is still an undocumented feature. |
can i used the precognition in blade file can any one explain with the example if all of you can help 'it's great request |
Precognition
Precognition (from the Latin prae- 'before', and cognitio 'acquiring knowledge'), is the purported psychic phenomenon of seeing, or otherwise becoming directly aware of, events in the future.
🔮 Source: Wikipedia
Image source: The "Precogs" from the film Minority Report
tl;dr;
Precognition is a new framework feature that will allow developers to create new and improved experiences for their users.
Precognition introduces a request / response header, middleware, and for some cases a global helper that Laravel reacts to in a unique manner.
When a Precognition request comes into Laravel, everything right up until the controller is executed. This includes all middleware, route model binding resolution, form request validation etc.
This unlocks new flows and possibilities for applications, such as:
And no doubt many more general and application context specific usecases.
And all of this is possible without creating any new routes, but by applying the new
Precognition
middleware to any route you would like this feature available on.What type of applications can benefit from this feature?
The only type of app that cannot tap into the benefits this feature offers is true Blade only applications that does not, and does not intend to, utilise any JavaScript, and Livewire applications, as I believe some of this functionality may already be available.
Example: Improving validation UX
I think the best way to understand this feature is to look at some examples. So let's dive right into using Precognition to give a better validation experience.
We'll work with the following sign up form as an example:
For this form, we have the following route and form request object.
...and things worked. But then when we want to improve the UX of this form, we now want to push for some more real-time validation, so that the user doesn't have to submit the form only to discover that their username has already been taken.
We could duplicate a lot of this state-less validation in the front-end -
min:3
andmax:16
are trivial to duplicate (☝️ but doing this does mean we need to keep things in sync).However, there is no way for our front-end to determine if the username is actually unique in our database.
With Precognition, we can anticipate the outcome of the form submission and determine if the
username
is in fact unique before the form is submitted. Here is how we would do that:First thing we need to do is opt-in to Precognition for this route by adding the
Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests
middleware:That is all that is required for the backend. Moving into the front-end we can add an "onChange" listener on the
username
field and send the following request - taking note of the "Precognition" headers:The
Precogntion: true
header tells Laravel that the client is attempting a Precognition request. ThePrecogntion-Validate-Only: username
header tells Laravel that the client only wants to run the validation rules for theusername
input.The code for the application then runs right up until the Controller would be invoked, which means the form request is resolved and it's validation process runs. If the
username
is not unique (or doesn't meet the other validation requirements), a validation response will be returned.Which the front-end can then utilise to notify the user long before the user has moved on and submitted the form (again, the front-end libraries make handling the validation error a breeze).
You will also note that although the other inputs are
required
, they did not return a validation response. That is because we requested that only theusername
input be validated.Alternatively, if the validation was successful a
201 No Content
response with aPrecognition true
header is returned.This allows the frontend to provide early feedback to the user about their username being valid.
So we have just allowed somewhat real-time validation on our form and improved the users experience. The user now gets feedback on their username before they have completed the form.
There are of course more considerations an application needs to make around rate limiting, debouncing requests, cancelling in-flight requests, and what is allowed to run during a Precognition request - but don't worry, we've sweated the small stuff and our front-end libraries handle all of this for you.
While we are discussing validation, there are some additional out-of-the-box validation features that are worth mentioning before we move on to other possibilities with Precognition.
Server-side Rule Filtering
We have seen how the client can ask for specific inputs to be validated, but what we haven't seen is how the server can specify which rules should be run during Precognition.
By default, all specified validation rules are run during Precognition, however it is possible to exclude validation rules from a Precognition request on the server side.
Let's say that we only wanted the back-end to validate the things we can not easily replicate on the front-end (the state based rules. We can tell the form request to exclude validation rules from Precognitive requests like so:
In the above form request, only the
Rule::unique('users')
rule would ever be against theusername
during a Precognition request. The client may still ask for only theusername
rule to be run, but when it does, all the otherusername
rules, such asmin
,max
, etc would not be executed.Of course, depending on your needs and usecase, you may actually find it is the stateless rules that you want to allow on Precognitive requests and leave the more performance heavy validation rules for the final submission. This would mean that the CPU footprint would be much smaller for your Precognition requests, as everything is handled in-memory while giving you some nice real-time validation without duplicating rules across stacks. You would just switch the above logic to achieve this.
The Framework can not handle application specific scenarios, so as we have seen the request provides a
$request->isPrecognitive()
function for use in your own code to determine if you would like to exclude certain functionality from a precognitive request.This is useful for middleware if you want to exclude some side-effect functionality from a precognitive request. Same goes for
after
validation rules.So at this point I'm hoping I've got you onboard with the idea and how you can work with validation ✅ so let's move onto some other possibilities.
Example: Detecting updated records
One problem often faced by applications where multiple users can access and edit the same information is what to do when a record is updated by another user while the original user is still editing a record.
This can lead to missing updates and all sorts of problems for your application. Lost data. Lost time. What. A. Pain.
Wouldn't it be nice if the application could instead see into the future and warn the user closer to when a conflict is created, rather than waiting for the form to be submitted?
Assuming the application has implemented this a conflict check in a middleware, something like:
The front-end may send Precognitive requests periodically to ensure that the resource has not been updated while it is being edited. Here is an extremely naive (read: don't copy and paste this) example of how to achieve this in a low level manner (without any overlaying sugar):
The front-end libraries make polling with Precognition a first-party concern.
Example: Detecting locked records
In contrast to the above where we detect conflicts after another user has edited a record, another nice usecase for Precognition is stopping two users from editing the same record at the same time - and also for letting users "take over" the resource if another user currently has it locked. This is a slightly different approach to the above example, but hopes to achieve a similar thing.
In a similar manner to the above example, the front-end could periodically check in with the server by performing a Precognition request and the server could respond with a
423 Locked
response if another use has locked the resource.This is interesting as it would allow users to ping pong the lock access. i.e. I could take over editing from another user and they would be notified that I locked the resource.
So after all that: Why Precognition?
Now that we have taken a look at some examples, lets talk about the feature generally. Let's face it, everything I've shown you is already possible with Laravel. We can already do these things...but in order to do them we need to create dedicated endpoints that don't trigger side-effects / duplicate code paths or we need to do a bunch of manual work with query parameters or something to handle things ourselves.
Precognition establishes patterns and makes those patterns first class citizens of Laravel to help enable developers to create amazing experiences for their users - just by adding a middleware to their route.
Advanced feature: Running code in the controller
Everything we have seen so far has shown you how things "just work" outside of the controller, however it may be the case that you like to keep things within your controller. To do this, you will need to extend the Precognition middleware and re-bind the standard controller dispatcher classes.
When you are using this option, it is then on the developer to ensure that they call the
precognitive
global helper when they want precognitive requests to stop (note that when filtering validation, the execution will still stop after calling$this->validate()
.Whatever is returned from the closure is returned to the controller. If you want to return multiple things, you can use destructuring:
The code in the clousure is executed for both Precognition and non-Precognition requests - however a Precognition request will always end once the closure has been invoked and return the "empty response", unless you have invoked the
$bail
function.Notes
"Vary"
header returned in Precognitive requests into account when deciding to cache responses.This is only needed for Blade apps with JS sprinkles, or Inertia apps.
Huge thanks to everyone on the Laravel team for all their help and feedback on this feature ❤️