There are occasions where it may be desirable to manually create a search URL.
Not me personally, but hey, everyone's different (apart from genetically identical clones, obvs).

Although XF does provide the search parameters in the output URL when using the built in UI, which will generate a new search query if clicked, there are instances where there are no params and just an ID.
For example, searching for all posts by a specific member, or members, just produces:, which will only work until the ID expires.

Yes, it is possible to get the search URL for a specific member from their profile page, but there is another way.

This guide then will explain how to manually craft permanent search URLs, which when clicked, will produce the same results as using the built in search forms and links.

Navigating to the main search page and inspecting the search_form template we can see that each field has a name="value" attribute.


From the screenshot above there are three (of the five) field names visible - keywords, c[users], and c[newer_than].
It is these name fields which are used to construct the URL.

So taking a basic example like so:


A manually created URL for that would be constructed as follows:
  • <board URL>/search/search
  • ?keywords=test
  • &c[users]=Brogan
Note that after the search route a ? is used for the first attribute, and subsequent attributes are added using &.

The resulting URL is therefore:
Oops! We ran into some problems.

Although that URL will work when pasting it into the browser address bar, when adding internal URLs to templates, navigation tabs, etc., it is preferable (but not mandatory) to use the {{ link() }} function.

This is where things get a little complicated.

If you have Friendly URLs enabled then it is possible to cheat and use this: {{ link('search/search') }}?keywords=test&c[users]=Brogan.

However, if you don't have Friendly URLs enabled then the syntax is a bit more complicated: {{ link('search/search', null, {"keywords": "test", "c": {"users": "Admin"}}) }}.

You should be able to see that all of the same elements are there but it's not as user friendly to construct.
You will have to decide which format works best for you based on how your site is set up and how much you want to torture yourself when composing the strings.

Once clicked, the code then performs the search and spits out the following URL: Search results for query: test.

You can see that the URLs differ but fundamentally it's the same thing:
  • a unique ID 123456 has been added
  • the keywords attribute has been changed to q
  • an additional parameter o=relevance has been added to the end of the URL - that is due to the Relevance radio box, which is a feature of XFES, being selected by default
Here's another example using one of the date fields and changing the order attribute:


A manually crafted URL would be this if using Friendly URLs: {{ link('search/search') }}?keywords=test&c[users]=Brogan&c[newer_than]=2021-05-01&order=date.

This if not: {{ link('search/search', null, {"keywords": "test", "c": {"users": "Brogan", "newer_than": "2021-05-01"}, "order": "date"}) }}.

See, I told you the non Friendly URL version is a bit lot more complicated!

The actual URL output is: Search results for query: test.

For the rest of the guide only the cheaty simple format will be provided in the examples.
Who uses index.php these days anyway?

What about multiple users and the other date field?


The example above would be: {{ link('search/search') }}?keywords=test&c[users]=Brogan,Chris+D&c[newer_than]=2021-05-01&c[older_than]=2021-05-26&order=date.

Multiple user names are separated using , and for user names with spaces in them, replace the spaces with +, so Chris D becomes Chris+D.

For those of you wasting electrons by appending index.php to the URL, this is the string: {{ link('search/search', null, {"keywords": "test", "c": {"users": "Brogan,Chris D", "newer_than": "2021-05-01", "older_than": "2021-05-26"}, "order": "date"}) }}.

Note that in this format, the space in the user name is preserved and not replaced with +.

Wait! I said I wasn't going to do another one.
Well that's your last one - you're on your own from here on out.

The corresponding URL produced by XF is: Search results for query: test.

The same concept can be applied to any of the search forms and fields.
Here's one which searches in multiple forums:


In this case it is necessary to obtain the forum IDs to pass into the attributes.

The ID can be obtained from the URL if a URL portion hasn't been set for the node.
In the case of the Installation, upgrade, and import support forum it's 23: Installation, upgrade, and import support.

However, that won't be available if a URL portion has been set.
For those cases, use the browser inspector to check the IDs like so:

Or just use the UI to perform a search and the node ID will be displayed in the URL for the c[nodes] attribute: Search results for query: bug.

So going back to the example for searching in multiple forums, this is what we end up with: {{ link('search/search') }}?keywords=test&search_type=post&c[child_nodes]=1&c[min_reply_count]=2&c[nodes][1]=23&c[nodes][2]=24&c[nodes][3]=25&c[nodes][4]=84&c[title_only]=1&order=replies.

Good luck writing that out in the non Friendly URL version!

XF produces this: Search results for query: test.

A few params and constraints which aren't obviously exposed are content, grouping, and thread_type.

For example, to only search the first post of threads, you still use search_type=post but you also pass in the content type with a value of thread: c[content]=thread.

So this URL would return results from both threads and posts: {{ link('search/search') }}?keywords=test&search_type=post&c[nodes][1]=23&order=date.

While this one would return results from threads (first posts) only: {{ link('search/search') }}?keywords=test&search_type=post&c[content]=thread&c[nodes][1]=23&order=date.

The grouping option searches all posts in a thread but only returns one result per thread.
An example of using that would be {{ link('search/search') }}?keywords=test&search_type=post&c[nodes][1]=23&order=date&grouping=1.

The thread_type constraint allows a keyword search to be performed on a specific thread type.
In this case, only results from threads which are set as poll type are returned: {{ link('search/search') }}?keywords=test&search_type=post&c[thread_type]=poll.

The same principles apply to other handlers and content types.

So that's all well and good but what's the point of it all?
Don't ask me, I just write the stuff.

Obviously the standard way to perform a search is to use the UI, but this approach can be used to create specific links, navigation tabs, buttons, it can be used in notices, widgets, etc.

For example, if for some strange reason you wanted a button on your site which would perform a search for a specific string in posts by two members between two dates in May, you could do:

<xf:button href="{{ link('search/search', null, {"keywords": "test", "c": {"users": "Brogan,Chris D", "newer_than": "2021-05-01", "older_than": "2021-05-26"}, "order": "date"}) }}">Click</xf:button>
Which is fairly pointless in and of itself, but I'm sure you, yes you, no the other one glancing from side to side, can think of some uses.

The easiest way to create a custom search URL is typically by performing the search and then reformatting the URL which is output using the instructions above.

If after creating the URL the results are not what are expected then double check the attributes.
Ensure that the correct names have been entered, for example order instead of o, and search_type rather than t.

Also be aware of the system limitations.
You can't for example do a global search for all threads with a specific prefix, or poll, without a keyword.
This will work for example: {{ link('search/search') }}?keywords=test&search_type=post&c[prefixes][1]=49&order=relevance.
Whereas this won't: {{ link('search/search') }}?search_type=post&c[prefixes][1]=49&order=relevance.
First release
Last update
0.00 star(s) 0 ratings

More resources from shimmer