4

I've read as many forum posts and documentation but am a little stumped over a specific use-case. Currently the search page grabs the url segment, loops through a verified user group, and uses the .shuffle() function to randomise order.

I'm looking to limit search results using relations with a customTagsField on a user's profile. It also needs to limit results using only the first 5 tags that the user has entered in customTagField, in the order they are arranged in control panel.

Only the first 5 tags are searched, in the order they're displayed

<div class="grid-item 1/1">
    <h1>Search Results</h1>

    {% set query   = craft.request.getParam('q') %}
    {% set results = craft.users.group('verified').search(query) %}
    {% set users   = shuffle(results) %}

    {% if users|length %}
        <p>{{ users|length }} results:</p>

        <div class="grid">
        {% for user in users %}
            <div class="grid-item 1/1 mb">
                <div {# class="grid-item xs-1/4 sm-1/6 lg-1/8" #}>
                        <img class="img-circle" src="{{ user.photoUrl }}" alt="">
                      </div>
                <h2 {# class="grid-item 1/1" #}>{{ user.fullName }}</h2>
                <div {# class="grid-item 1/1" #}>{{ user.intro }}</div>
                        <div {# class="grid-item 1/1" #}>
                            <a class="btn" href="/profile/{{ user.username }}">View counsellor</a>
                        </div>
                    </div>
        {% endfor %}
        </div>
    {% else %}
        <p>Your search for “{{ query }}” didn’t return any results.</p>
    {% endif %}
</div>

I'm still a bit confused about relatedTo as well as source and target terminology, even though I've read quite a bit about it!

Is it as simple as changing results to:

{% set results = craft.users.group('verified').relatedTo(customTagsFieldHandle).search(query) %}

It doesn't seem possible to limit the tags searched using relatedTo (as you can with craft.tags, so I expect I'll have to edit the for loop somehow. I know about Twig's slice function but I'm not sure how to go about limiting by a user's tag entries.

Does anyone have suggestions for the optimal way to achieve this?

  • To clarify, you’re saying you have a search form, and you want it to search for users whose selected “Specialism” tags match the search query; but you only want the first 5 tags to be factored in, and any additional tags should be ignored. Correct? – Brandon Kelly Oct 6 '15 at 13:28
  • @BrandonKelly Got it in one, that's exactly what I'm trying to do. Just to clarify it's the 5 tags in the same order they've been arranged in CP. We may in future allow premium members to be searchable for more than 5 tags (likely through another user group) but for now I'm keeping things simple. Tags seemed to be the most sensible choice to have a selectable list of specialties. – Rob Oct 6 '15 at 20:26
3

There’s no way to tell the relatedTo param to only factor in the first X relations per source element (the first 5 tags per user, in this case).

I can think of two ways you could accomplish this:

Option 1: Use two Specialism fields

Replace your “Specialism” Tags field with two Categories fields – “Primary Specialism” and “Secondary Specialism”. Limit “Primary” to 5 categories, so no one can ever enter more than 5 specialties in there, and they can list any additional specialties in the “Secondary” field.

Then from your template, you can limit the search query to only factor in the “Primary” field:

{% set results = craft.users({
    group: 'verified',
    relatedTo: {
        targetElement: craft.categories({
            group: 'specialisms',
            search: query
        }),
        field: 'primarySpecialism'
    }
%}

This method will get more complicated if you do start allowing premium members to be found from both fields down the road; you would need to split it up into two separate user queries – first for the premium members, and then again for the normal members.

Option 2: Automatically store the searchable specialisms in a separate, hidden field

Install the excellent Preparse plugin, and create a new Preparse field called “Searchable Specialisms” with the following Twig code:

{% spaceless %}
    {% for tag in user.specialismTags.limit(5) %}
        {{ tag.title }}
    {% endfor %}
{% endspaceless %}

(Disable the “Show Field” setting so this field doesn’t show up on user profile pages. {% spaceless %} is required to remove initial whitespace character from the newly parsed “Searchable Specialisms” field)

Once the field is created, add it to your User field layout (Settings > Users > Fields) - doesn’t matter where.

Finally, update your set results template code to this:

{% set results = craft.users({
    group: 'verified',
    search: 'searchableSpecialisms:"' ~ query ~ '"'
}) %}

This approach will make it much easier to handle the premium member feature down the road, because you’ll just need to factor in whether they are a premium member or not in the Preparse field settings:

{% set limit = user.isInGroup('premium') ? null : 5 %}
{% for tag in user.specialismTags.limit(limit) %}
    {{ tag.title }}
{% endfor %}
  • Thanks for this Brandon. Option 1 isn't really suitable, to me categories should be limited and there could be 100s of possible conditions the users might add. Option 2 looks like it will do the job! I didn't realise limit() could be used on tags (is this a better solution than user.specialismTags[0:5]?) Questions: 1) There's mention of a gotcha, the user will save out their profile so this won't apply right? 2) I've not seen _results.html written like this before — is there a specific reason to not use dot notation? – Rob Oct 8 '15 at 17:39
  • You can use the limit param any time you’re dealing with an ElementCriteriaModel, which is what all the relational fields return. It’s better than the [0:5] syntax because that will cause Craft to query the full set of tags and then discard all but the first 5. limit will actually limit the SQL query. ; That gotcha shouldn’t matter in this case, right. ; Not sure what you mean by _results.html? – Brandon Kelly Oct 8 '15 at 18:19
  • Great! I'll use limit in future. By _results.html I'm referring to your example demo site and the docs where you use .getParam('q') and .search(query). What is the benefit of using your set results method and where can it's documentation be found? – Rob Oct 8 '15 at 19:08
  • Added a couple of changes to your code, seems to be working well! The only issues I can see are that partial words don't return any results (for instance anx will not search for anxiety, so it has to be exact. I guess you could always pre-search and use a plugin like Chosen to prepopulate the search form. Also, do you have any suggestions to properly {% cache %} the results? – Rob Oct 9 '15 at 19:53
  • @Rob Partial searching is possible via anx*. (See buildwithcraft.com/docs/searching for all of the possible search syntaxes.) Can you post your caching question in a new Q&A thread? – Brandon Kelly Oct 9 '15 at 23:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.