Membership System - Enhanced Search Capability

Enhancement on the search capability with keyword search to provide better usability for cashier.

#laravel , #keywordSearch

Published on: 08 Feb 2021 23:27pm

As we have introduced the search function previously, our cashier is pretty limited to only able to search member by ID.

What if the members forgot their member ID or they did not bring their membership card along?

Since we have captured some of the member's information in the database, let's introduce a new search function to allow cashier to search member by using name, email, or contact number.

New Search Capability

Let's create a new enhanced search form in the same page.

We assume most of the members will still be using the member ID, we'll maintain the previously created search bar, and we create this new search bar in second tab.

Note

Putting UX into consideration, we assume our cashier would be using this new enhanced search bar less frequently than the original one, with the assumption that most members will remember their IDs. However, this is purely based on assumption that most members will remember their member ID's, and we can only find out by collecting usage data.


1 - Enhancing Search Interface

Let's update the public/index.blade.php

    <!-- resources/views/public/index.blade.php -->
    <ul class="nav nav-tabs">
        <li class="nav-item">
            <a href="#memberId" class="nav-link active" data-toggle="tab">Scan ID <span class="fa fa-qrcode"></span></a>
        </li>
        <li class="nav-item">
            <a href="#search" class="nav-link" data-toggle="tab">Search <span class="fa fa-search"></span></a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane fade show active" id="memberId">
            <div class="card card-body">
                <p>Scan member ID</p>

                <form action="{{ route('public.scan') }}">
                    <div class="form-group">
                        <label for="memberIdInput">Scan Member ID</label>
                        <div class="input-group">
                            <input type="text" class="form-control form-control-lg" id="memberIdInput" name="memberId">
                            <div class="input-group-append">
                                <button class="btn btn-primary btn-lg">Search</button>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
        <div class="tab-pane fade" id="search">
            <div class="card">
                <div class="card-body">
                    <p>Search members by name, email or contact number</p>
                    <form action="{{ route('public.search') }}">
                        <div class="form-group">
                            <div class="input-group">
                                <input type="search" class="form-control form-control-lg" id="searchInput" name="query">
                                <div class="input-group-append">
                                    <button class="btn btn-primary btn-lg">
                                        <span class="fa fa-search"><span>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

Explanation:

  1. We've used the Bootstrap's Nav Tabs components to build the tab function.
  2. In second tab, we've added a new search form which will submit the data to route('public.search').

Now we have to define the route for the route('public.search').

    /* routes/web.php */
    Route::get('/search', [App\Http\Controllers\PublicController::class, 'search'])->name('public.search');

Let's refresh the page to see the newly created form.

Enhanced search form in second tab
Enhanced search form in second tab

2 - Search Logic

Let's define the search logic in our controller function.

    /* app/Http/Controllers/PublicController.php */
    public function search(Request $request)
    {
        $query = $request->input('query');

        $members = Member::where('name', 'like', '%' . $query . '%')->orWhere('email', $query)->orWhere('contact_number', $query)->get();

        return view('public.search')
            ->with([
                'query' => $query,
                'members' => $members,
            ]);
    }

Explanation:

  1. When the form is submitted, the keyword is passed as $query to this controller function.
  2. We've used the Member Eloquent model to retrieve any members that have either name that contains the keyword $query, or email address or contact number that is exactly matched with the $query. This is where the search logic takes place.
  3. Once the members data are retrieved, then we've used the view('public.search') to render the search result page, which we'll create later.

Note

The query method we've used is a keyword-based search, which is simply querying the database and find the data that contains the keyword, by using the like %keyword% SQL query string.


Search Results Page

We'll need to render the list of suggestion after client search using the keyword.

Let's introduce a new Search Results page. We'll create a public/search.blade.php.

    <!-- resources/views/public/search.blade.php -->
    <p>Showing results for "<b>{{ $query }}</b>"</p>
    @if(count($members) > 0)
    <table class="table table-striped">
        <thead class="thead-dark">
            <tr>
                <th>Member ID</th>
                <th>Name</th>
                <th>Email</th>
                <th>Contact Number</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            @foreach($members as $member)
                <tr>
                    <td>{{ $member->id }}</td>
                    <td>{{ $member->name }}</td>
                    <td>{{ $member->email }}</td>
                    <td>{{ $member->contact_number }}</td>
                    <td><a href="{{ route('public.scan', ['memberId' => $member->id]) }}">Proceed &gt;</a></td>
                </tr>
            @endforeach
        </tbody>
    </table>
    @else
    <p>No result for "{{ $query }}".</p>
    @endif

Explanation:

  1. We've listed down the members found using the table layout.
  2. In each loop, we've used route('public.scan', ['memberId' => $member->id]) to link to the member's information page. Note that we've passed the memberId parameter to this route as well.

Once done, we're ready to see the final results. Let's use the search bar to search for "tester". All the members with name that contains "tester" keyword will be listed in the search results page.

Search results of 'tester'
Search results of 'tester'

We can also try to search by email address or contact number. Note that searching by email address or contact number should be actual match. Hence, any misspelling on email address / contact number will not return any results.

No result when no exact match
No result when no exact match

Conclusion

That's pretty much sum-up of how we use the enhanced keyword based search to search for members from the database.

So far, our membership system is capable to serve the basic functionality and should be ready to be installed any POS system as a Minimum Viable Product (MVP).

Next step...

Moving forwards, we'll start looking into enhancing internal and administrative functionality. In next post, we'll introduce the membership expiry date and how the administrator can manage the duration of membership.