CakePHP how to write a search form to display results - forms

I am writing a search form in CakePHP 2.0, current I have set it up running with the index action and view (it also posts to the index action) with validation against the model so that if anything incorrect is entered into a search field (fields include date, price) there is a nice validation error message next to the element. Basically it is a bit like a scaffolded add form.
If validation is successful I need to actually run a query and return some data. I don't want to display this data in the index view - should I:
Run the query then render a different view (which means the URL doesn't change - not sure I want that).
Store the search parameters in a session, redirect off to another action then retrieve the search details.
Is there any other way?

Both options are ok. You must decide what you like more, to not change the url or to change it?
you may also use the named parameters to pass the info so a user can bookmark their request, though it would need to do the validations in the same page as where it shows results. I usually do this with the cakedc search plugin.
Returning to your two options, if you mean which is better in performance i would choose number one, since the second one needs to load a new model/controller etc

Related

How to combine pagination with getting next / previous element of a REST collection

I am trying to design a REST API in which there is a paginated collection. In my current design I use a page based approach:
GET /entities?page=2&pageSize=25
Retrieving a single entity is trivial:
GET /entities/4
A user story related to this API requires that when viewing a single entity in a screen, two buttons "Previous" and "Next" enable switching to said entities. An example:
GET /entities?page=2&pageSize=25
returns:
[{id: 2}, {id: 4}, {id: 17}]
Now, when viewing entity with id 4, the named buttons would navigate to the entities with id 2 or id 17.
My assumption is, that a client (web frontend in my case) could be able to "remember" the pagination information and use that when fetching the previous or next entity. This could be applied to eventual filters which I might add to the endpoint.
A basic idea of how to implement this would be to get the current page and for edge cases the previous and the next page (required if currently viewing the first / last resource of the collection). But that seems like an inefficient solution.
So my question is: Is my chosen pagination method even compatible with what I try to archive? If it is, how would clients use the API to archive the next/previous feature?
In this case I ended up with a frontend based solution.
Given that my constraint was that I could not change the type of pagination used, this is a crude solution:
Frontend makes initial GET request on the paginated data. This request may not only contain pagination parameters (page + size) but also filters (color = red). This is the search context.
The user accesses one of the items and some other view is displayed. Important: The search context is kept in local storage or similar.
If the user wants to browse to the next / previous item, the search is executed again with the same search context.
In the search result, search for the ID of the currently displayed item
If the currently displayed item is found, show the next / previous item
If it is not found, do some fallback. (In my case I chose to simply display the initial search UI)
I am not happy with this implementation as it has some major drawbacks IMO:
Not efficient: For every browsing click, a search request is made. When not using an efficient search database like Elasticsearch this will probably cause a lot of stress on the db.
Not perfect: If the currently displayed item is not in the search result, there is no sensible way to get the next / previous item
If the currently displayed item is the first / last item in the search result and you want to browse back / forward, the search would have to be executed twice, once on the current page, once on the previous / next.
As stated, I think this is not a good solution, I am still looking for a clever, efficient way to do this.

Should I allow user-provided values to be passed through a query string?

I'm adding a search endpoint to a RESTful API. After reading this SO answer, I'd like the endpoint to be designed like:
GET /users?firstName=Otis&hobby=golf,rugby,hunting
That seems like a good idea so far. But the values that I'll be using to perform the search will be provided by the user via a standard HTML input field. I'll guard against malicious injections on the server-side, so that's not my concern. I'm more concerned about the user providing a value that causes the URL to exceed the max URL length of ~2000 characters.
I can do some max-length validation and add some user prompts, etc, but I'm wondering if there's a more standard way to handle this case.
I thought about providing the values in the request body using POST /users, but that endpoint is reserved for new user creation, so that's out.
Any thoughts? Thanks.
I see these possible solutions:
not actually a solution. Go with the query parameter and accept the length constraints
go with the POST solution that shouldn't be designed as you mention. As you point out, if you POST a user to .../users you will create a new user entity. But this is not what you want to do. You want to submit a search ticket to the server that will return a list of results matching your criteria. I'll design something as such
POST .../search/users passing in the body a representation of your search item
distribute the query both server side and client side. Say you have complex criteria to match. Set up a taxonomy of them so that the most strict ones are handled server side. Thus, the server is able to return a manageable list of items you can subsequently filter on the client side. In this approach you can save space in the query string by sending to the server only a subset of the criteria you want to meet in your search.

Elastic Search completion suggester configuration

I'm trying to set autocomplete/suggestions on my site's search form, using Elastic Search's completion suggester feature.
I have a list of products, which are grouped by categories (on multiple levels). The search feature should be able to suggest category names, which are of more interest to users than direct products.
Several of these categories have the same name but a different parent (e.g. 'milk' under parent category 'dairy products' and 'milk' under category 'baby'). When the user selects a category suggestion, she's redirected to another page, with more specific results than mere search method.
Additional metadata (url to redirect to, parent category id/name) are added in the payload field.
I use the output field to return normalized suggestions for different inputs. As stated in the documentation:
"The result is de-duplicated if several documents have the same output,
i.e. only one is returned as part of the suggest result."
But as explained, my outputs may have the same value, while being different results, as they will link to different pages. It is also possible in the future that different category levels will yield different actions.
I am reluctant to differentiate things by adding the full string (i.e. "milk in dairy products") as the output, because:
1. The parent category is conceptually not the output itself but a related metadata.
2. I intend to have some formatting in the results, this forces me to parse the output string to add HTML tags in it.
So, is it possible to deactivate the de-duplication?
One workaround I'm thinking of if it's not possible is to store a stringified json object in the output, with all the data 'll need, both the one displayed in the search form and the metadata currently in the payload. But Id' rather look into existing configuration before resorting to that.

How do I trigger form validation without binding a request?

I have a very long order form that enables saving drafts. If saved as draft, only order name is required but when actually placing an order a more thorough validation is required. I implemented this by using different validation groups. When editing the order I display two buttons: "Save draft" and "Place order". Each of them performs validation using a different validation group.
But now I would like to make a button on the list of orders which enables to change order status from 'draft' to 'placed' directly. To do so, validation must be performed without displaying edit form and submitting it. I would just like to validate the entity that is already in the database. I can use the validator service and everything is simple as long as the data is valid. But in case data isn't valid, I would like to redirect user to the edit form with fields with missing data highlighted. The idea seems to load data from database into the form and run validation as if that data were sent using a browser but execution of this doesn't seem to be trivial because Symfony2 triggers validation on form only when binding the request.
I was going through the Symfony source code and found s class called Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener. It seems to attach itself on the FormEvents::POST_SUBMIT event. Is there a way to trigger this event manually from the controller without request binding? Or are there any alternative approaches to my problem?
Just to point out the correct answer already given by Matjaž Drolc in the comments:
If you want to validate a form without getting the data from the request, you have to call the form->submit() function, because Symfony does not validate the fields if they are not marked as submitted, which is done by this function.
Call the function like this
$form->submit(array(), false);
With an empty array as the submitted data and not clearing the missing fields.

InfoPath Form Reset Main Data Connection

I'm building an InfoPath form connected to a SharePoint List.
After the user has filled out a couple of fields (which can uniquely identify a record), I re-query the main data connection to see if the item already exists. This works fine and loads the remaining details of the existing record into the form.
However, if the record does not exist, all the fields on the form become disabled... how can I reset the main data connection back to its initial state to allow a new item to be submitted?
You will need to set a condition rule. So for example on the last field the do an insert on do your query, then in your rules (you may need multiple depending on how many fields) , saying if field is (whatever) then do (whatever). The disable features are set in the formatting section of your rule.