Employee Directory with PHP and Laravel

Download the Code

Learn how to implement an employee directory that you can query using SMS. Request information from anyone at your company just by sending a text message to a Twilio Number.

Here is how it works at a high level:

  • The user sends a SMS with an Employee's name to the Twilio number.
  • The user receives information for the requested Employee.

Handle Twilio's SMS Request

When your Twilio Number receives an SMS, Twilio will make a POST request to /directory/search asking for TwiML instructions.

Once the application identifies one of the 3 possible scenarios (single partial match, multiple partial match or no match), it will send a TwiML response to Twilio. This response will instruct Twilio to send an SMS Message back to the user.

Loading Code Samples...
Language
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
app/Http/Controllers/DirectoryController.php
Employee Directory Controller

app/Http/Controllers/DirectoryController.php

Let's take a closer look to each one of the scenarios.

Single Employee Match

This is the simplest scenario. We'll verify that only 1 match is obtained. If a single match is found, a message containing this employee's information is built and sent to Twilio as TwiML instructions.

Loading Code Samples...
Language
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
app/Http/Controllers/DirectoryController.php
Single employee result

app/Http/Controllers/DirectoryController.php

If multiple matches are found we'll try to do a multiple partial match. That is our next possible scenario.

Multiple Employee Matches

At this point we have already tried to use the user's query as a single partial match. Now we'll try to get a partial match that returns more than one result. We'll use Twilio Cookies to store suggestions. The only difference here is that we use a list to store suggestions. This way the user can reply with a number that references one of the suggestions in order to get the employee's information. The way this information is stored will be explained on the next step.

The last scenario is simple. If none of the previous scenarios occur, it means that there is no employee in the database that matches the user's query. In that case, a reply will be sent to the user explaining that their query doesn't match any of the employees found on the database.

Loading Code Samples...
Language
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
app/Http/Controllers/DirectoryController.php
Return list of employees

app/Http/Controllers/DirectoryController.php

Next, we'll see how we use cookies in Laravel to cache search suggestions.

Store Suggestions With Cookies

When a user gets a partial match by searching the Employees Directory, we reply with one or more suggestions. We need to store these suggestions. This way the next time the user sends an SMS we know this is not a query for a new employee, but a selection of one of the suggestions.

We'll use Twilio Cookies and a Laravel Session to store suggestions. They will allow you to keep track of an SMS conversation between multiple numbers and your Twilio powered application.

Loading Code Samples...
Language
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
app/Http/Controllers/DirectoryController.php
Store employee names in Flask session

app/Http/Controllers/DirectoryController.php

That's it! We have just implemented employee directory using Twilio and Laravel. Now you can get your employee's information by texting a Twilio number.

Where to next?

If you're a PHP developer working with Twilio, you might also enjoy these tutorials:

Automated-Survey

Instantly collect structured data from your users with a survey conducted over a call or SMS text messages. Let's get started!

ETA-Notifications

Learn how to implement ETA Notifications using Laravel and Twilio.

Did this help?

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio to let us know what you think!

Mario Celi
David Prothero
Agustin Camino
Jose Oliveros

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Employee;
use App\Http\Requests;
use App\Http\Session;
use App\Http\Controllers\Controller;
use Twilio\Twiml;

class DirectoryController extends Controller
{
    public function search(Request $request)
    {
        $body = $request->input('Body');
        if ($this->_isChoiceAnswer($body, $request)) {
            return $this->_selectedEmployee($body, $request);
        }

        $query = Employee::where('full_name', 'LIKE', '%' . $body . '%');
        $count = $query->count();
        if ($count == 1) {
            return $this->_singleResult($query);
        } elseif ($count > 1) {
            return $this->_multipleResults($query, $request);
        } else {
            return $this->_notFound();
        }
    }

    private function _selectedEmployee($body, $request)
    {
        $email = $request->session()->get('employees')->get($body - 1);
        return $this->_singleResult(Employee::where('email', $email));
    }

    private function _isChoiceAnswer($body, $request)
    {
        return is_numeric($body)
            && in_array(
                intval($body),
                range(1, $request->session()->get("employees")->count())
            );
    }

    private function _singleResult($query)
    {
        $twiml = new Twiml;
        $employee = $query->first();
            $twiml->message(
                collect(
                    [$employee->full_name, $employee->phone_number,
                    $employee->email]
                )->implode("\n")
            );
            return $this->_xmlResponse($twiml);
    }

    private function _multipleResults($query, $request)
    {
        $twiml = new Twiml;
        $employees = $query->get();
        $request->session()->put(
            'employees', $employees->map(
                function ($employee, $key) {
                    return $employee->email;
                }
            )
        );
        $employees_message = $employees->map(
            function ($employee, $key) {
                $option = $key + 1;
                return "$option for $employee->full_name";
            }
        );
        $twiml->message(
            collect(
                ['We found multiple people, reply with:',
                $employees_message, 'Or start over']
            )->flatten()->implode("\n")
        );
        return $this->_xmlResponse($twiml);
    }

    private function _notFound()
    {
        $twiml = new Twiml;
        $twiml->message('We did not find the employee you\'re looking for');
        return $this->_xmlResponse($twiml);
    }

    private function _xmlResponse($twiml)
    {
        return response($twiml, 200)->header('Content-Type', 'application/xml');
    }
}