-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
REST API: Introduce reorder menu endpoint #25093
Comments
Useful context #21964 (comment) |
@spacedmonkey Just flagging this one for you here as you asked me to keep you in the loop on REST Menus API items. This is high on the priority list for the Navigation Editor in Gutenberg and I'm hopeful that someone will be able to take a look into it very soon. |
While thinking over this ticket I had some thoughts.
If allow for random menu items to be admitted, then you may still try and save two menu items in one position. It only works, if we got the whole menu and validate in one got go before saving anything.
Should it return multiple WP_Errors, adding additional_errors
Or should it return an error state with a response like this
I think the last one is the most usable response, but we don't have this output elsewhere in the REST API.
Thoughts @TimothyBJacobs, @getdave @draganescu |
It surely is easier if we send all the menu items in one go and I think it would be a good first version. That being said, I think it's possible to make it work with just a partial input. Imagine the following menu:
Then, a request like
Could follow the following algorithm:
After the entire operation, the menu looks like this:
Rinse and repeat for each reordered menu item (well that could be slow, but there's also a lot of optimization avenues such as doing the computations in memory first). For invalid inputs such as
We would just throw a validation error.
Good point! So now we have two use cases:
Since moving to another parent requires you to choose a position, even if the last one, I would say they are the same use case called "Moving a menu item to a specific position under a specific parent node". In which case the input would maybe look more like:
Now this looks "batchable", but I vote against using batch requests here. We need to reason about the input as a whole (at least to validate it) and that's not what batch requests are for. |
My biggest problem here is this: We have one tree stored on the backend and another one in the editor where some items were added, some are missing, we also have some new parent nodes with both new and existing menu items. We want to take the editor tree and save it in the backend. One way to do it would be having an endpoint to diff the input tree against the database tree. Another way would be to have an endpoint that scrapes all the stored menus and create new ones every time. And then, the easy way would be:
If then, in the If we continue to create new draft menu items in the editor right when the user clicks |
This format makes sense to me, though we'll need to use In terms of error response, I think we should probably reuse the way parameter validation works. So have in the data property something named
I don't think we can really do this. That'll cause a lot of entities to be deleted and recreated. Any references plugins are making to those menu items would also end up being lost.
This makes the most sense to me. We may want to allow creating them as |
I have been considering this endpoint over the last couple of days. Originally, when I built this endpoint, I wanted avoid doing more than one ( two with post meta ) database inserts / updates in a single REST API request. As in, one request = one database operation = one menu item. But this basically means that validation against other menu items is impossible. When considering the a reorder endpoint, by it's nature, it will update multiple rows, as more than one menu item can / will be updated in one request. At the moment, we are only updating menu order field and maybe parent id. Why this, point, if we are going to do a update, when why update other fields? Consider the following.
Or a much simpler solution, would be a do all the create, delete and update in one request. This is currently how the customizer admin ajax works. This has been in core for a long time and seems to have worked well. We could copy / reimplement the same code. I think we can only prepare for database failure up to a point. Databases failing is an edge case, for the most part, it should not block us for building a solution. |
We can do this now with the batching endpoint as it exists if we drop menu order validation. From the initial issue description:
|
Okay, if existing menu screens do not do this level of validation, then I think we do not bother with this endpoint. I think we should work on converting the current screen to using existing apis with batching. From there we can do some testing, to see if this endpoint is even needed. I would recommend doing validation like this in client. Maybe before making request, saving a version of menu data in local storage before making API requests, so if browser crashes, we can recover the data. After all the batch requests are completely, it would be simple enough to get all the menu items ( with a rest api request) again and comparing to local state to ensure they match. |
That works for me. Do you want to open a PR to remove that validation? |
Let's create a ticket first, then I will work on the PR. |
This sounds like a good version 1. My only concern is the usual suspect: network problems and lost updates. Unlike widgets, it's not uncommon to have large menus. This means more requests to batch and, in turn, more actual HTTP requests. But that's not specific to this ticket – it's a general thing related to batching. It may be a good reason to add a "preflight" validation check: If we need to split a batch into two or more API requests, we'd validate all the payloads first and only pass them to handlers if that worked. So for every "bundle" of Or we could just accept that we're not transactional for now, and defer this to some later point where we'd discuss transactions or changesets in some form. |
I was wondering, since we don't support the built in transaction mechanism in Mysql, if there is an option that for batched requests to be implementing a simulated transaction:
For this we'd need to store the batch data up to length in some option or CPT. So by having this approach we are free to send hundreds of requests and not fear that some of them will be lost which in turn breaks the logic of the bigger batch that happens to contain more than 25 steps. By storing the batch objects we can even retry things if they go awry because we can start again. How awful is this idea? |
Just a friendly reminder here, you can only send 25 request at a time in a batched request. See this line. Meaning only 25 menu items could be updated / deleted / created in one request. This means for a super large merge menu, like 200+ items, would still have to break this into at least 8 https / api requests. This also means, that if one of the API requests fails, it is easier to find where something went wrong, you can retry and even try sending one request at a time, it re-sending the batch request fails. It is worth thinking about how menu items could be broken into groups. For example, could we group of the delete, creates and edits into one request? Or just group them by menu order or id? |
In response to this. Let's think of responses why a REST API call might fail.
Batching requests to 25 items is by design. There are a number of limitations in PHP, 30 second execution time for one, some hosts limit the number database operations per quest etc. By batching this, it is much more likely that the saving of menu items will save correctly and not fail. It is also worth noting that existing menus screen as had long standing issues, with number of posted variables.
A preflight validation check, guesses that data provided might be invalid. Why would the data generated by the React code be invalid? Doesn't that seem very unlikely? |
I've given this some more thought (along with Jonny and Adam) in the context of eventual consistency problems b/c of batching limitations. In the end, I think moving ahead with the most common usecase is probably the best idea in terms of the API's resilience to failure. Not many menus are outside of the 25-50 items bracket. While the number of operations can be higher than the number of menu items, it's still just a few batch requests. While batching in fixed numbers of operations runs the risk of having half the operations saved and half not saved, buffering all batch calls runs the same risk if the server times out. Plus, the current screen already has the problem of timeouts. And the only way to have a sort of "rollback" is to use the Customizer and rely on changesets, but even then, it's not guaranteed that unusually complex menus in unusual server or connection situations won't end up saved incomplete. Therefore, let's move forward with batching as is. One thing it has going for it is that the number of operations to be sent at one time can be configured at implementation time, so unusually complex systems of great hardware can just increase this limit. Considering that it looks like we're going to drop the validation for order, hence we won't need this ordering endpoint, I'll leave it to @spacedmonkey to close this one. |
Just giving a +1 to what @spacedmonkey said. |
As for the way forward now, I agree. Let's do most out of what we already have and then iterate based on what we learn. I started a PR to migrate the editor to current REST API endpoints: #34541 As for the top-down design discussion, some additional points:
Also network connectivity issues could lead to losing one of the requests.
We can't do that if "Move menu item 2 from parent 4 to parent 5" fails, but "Delete parent 4" succeeds. Also, I'm not sure how to tell the user that all his updates went through except for the 4 menu items here. Do we rollback that part of the UI? Do we mark these items using red? What if the partial failure made the website non-navigable, even if for a few minutes?
Bugs happen, invalid inputs happen – backend validation reduces the chances of corrupting the data. |
Is your feature request related to a problem? Please describe.
The Menus endpoint makes it difficult to reorder multiple menu items. This is because the position of each menu item is validated sequentially checking that another menu item doesn't exist with the new position. So if you try to switch the position of two menu items, you'll get an error.
See #21964.
Describe the solution you'd like
As suggested by @spacedmonkey, introduce a REST API endpoint specifically for reordering menu items. Where
50
is the menu item id, and1
is its order.Describe alternatives you've considered
Drop menu item order validation altogether in the endpoint. This isn't validated in
WP_REST_Posts_Controller
. Though based on my reading ofwp_nav_menu
, a duplicatemenu_order
is more of an issue for menus since duplicates will not be printed in the menu.That being said, AFAICT neither the customizer endpoint nor WP-Admin validate the menu order.
The text was updated successfully, but these errors were encountered: