Get Started

Company Directory

Most company phone systems have the idea of "extensions", an additional to the companies public phone number that lets you connect to one of many company phones that sit behind that phone number.

For example: 415-123-4567 x1234

It's a simple matter to construct such a system with Twilio. In this article I will show how to build a simple virtual phone directory that will allow your customers to call your public phone number, enter an extension, and be connected to the corresponding number. It consists of two parts: a database table containing a mapping of extensions to phone numbers and a PHP application that serves up the TwiML to drive the voice menus and direct the call.

Download

companydirectory.zip

Table of Contents

Create a Company Directory Database

First, we will set up the database. Create a database called "company_directory" and run the following sql statements against it:

  • howtos/companydirectory/company_directory.sql
    CREATE TABLE directory (
      extension varchar(8) not null primary key, 
      phone_number varchar(32) not null
    );
    
    INSERT INTO directory (extension, phone_number) values ('0','415-555-1111');
    INSERT INTO directory (extension, phone_number) values ('1234','415-555-2222');
    INSERT INTO directory (extension, phone_number) values ('4321','415-555-3333');    

This will create a table to hold our extension to phone number mappings and populate the database with three example extensions. In this example extension "0" will be treated as the special case default for the operator/receptionist's line. You should add, update, or delete these rows to match your companies data

Next, lets tackle the application code. First we'll write some code to lookup a phone number given an extension. Replace the example database constants with your own information.

  • howtos/companydirectory/db_config.php
    <?php
    
    //DB Constants - Change to your settings  
    $db_host='localhost';  
    $db_name='company_directory';  
    $db_user='db_username';  
    $db_passwd='db_password';
    
    ?>    
  • howtos/companydirectory/extensions_map.php
    <?php
    	
    	require_once('db_config.php');
    	
    	function getPhoneNumberByExtension($ext){
    		global $db_name, $db_host,$db_user,$db_passwd;
    		mysql_connect($db_host, $db_user, $db_passwd)
       			 or die('Could not connect: ' . mysql_error());
       			 
       		mysql_select_db($db_name) or die('Could not select database');
       		
       		// Performing SQL query
    		$query = 'SELECT phone_number FROM directory where extension = '.mysql_real_escape_string($ext);
    		$result = mysql_query($query) or die('Query failed: ' . mysql_error());
    		
    		$phone_number=false;
    		
    		if($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
    			$phone_number= $line['phone_number'];
    		} 
    		mysql_close();
    		
    		return $phone_number;
    	}
    	
    ?>    

We've defined a function for doing the extension->phone number lookup. If you want to use a different storage mechanism for the mappings, you can rewrite the getPhoneNumberByExtension function without needing to change the rest of the application code.

First lets define the sql for the database table which will store the actual people in our company:

  • howtos/companydirectory/directory_search.sql
      CREATE TABLE people (
        firstname varchar(32) not null, 
        lastname varchar(32) not null, 
        extension varchar(8) not null,
        digit_index varchar(32)
      );
    
      insert into people (firstname, lastname, extension) values ('john', 'smith','1234');
      insert into people (firstname, lastname, extension) values ('eric', 'anderson','5555');
      insert into people (firstname, lastname, extension) values ('will', 'example','9124');
        

We create a table with the columns we discussed. You'll notice in that we did not populate the digit_index field in the test data inserts. Instead, we're going to run an index script that will build the index for us. If you want, you can easily modify this script to index data in your own tables.

  • howtos/companydirectory/build_index.php
    <?php 
    
    require_once('db_config.php');
    
    function stringToDigits($str) {
    	$str = strtolower($str);
    	$from = 'abcdefghijklmnopqrstuvwxyz';
    	  $to = '22233344455566677778889999';
    	return preg_replace('/[^0-9]/', '', strtr($str, $from, $to));
    } 
    
    mysql_connect($db_host, $db_user, $db_passwd)
                    or die('Could not connect: ' . mysql_error());
    
    mysql_select_db($db_name) or die('Could not select database');
    
    // perform a query to get all unindexed rows
    $query = "SELECT extension, lastname FROM people where digit_index is null";
    $result = mysql_query($query) or die('Query failed: ' . mysql_error());
    
    
    //iterate through each row, calculating the index and updating
    while($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
    	$exten = $line['extension'];
    	$lastname = $line['lastname'];
    	$index = stringToDigits($lastname);
    	$query = sprintf("update people set digit_index='%s' where extension='%s'",$index,$exten);
    	echo "Indexing extension $exten: $lastname -> $index\n";
    	mysql_query($query) or die('Query failed: ' . mysql_error());
    }
    
    mysql_close();
    
    ?>
        

Next, lets begin the voice application portion. First, we're going to need to define a function for doing the database lookup for the search. This function takes a set of digits and searches the people table for a record who's digit_index begins with the same digits. For example, if you search for people who's last name starts with "SMI", the application will match the first index that starts with 764. If there were no matches, the function returns false

  • howtos/companydirectory/search.php
    <?php 
    
    require_once('db_config.php');
    
    
    function search($digits){
              global $db_name, $db_host,$db_user,$db_passwd;
    
              mysql_connect($db_host, $db_user, $db_passwd)
                    or die('Could not connect: ' . mysql_error());
    
              mysql_select_db($db_name) or die('Could not select database');
    
              // Performing SQL query
              $query = "SELECT extension, firstname, lastname FROM people where digit_index like '".mysql_real_escape_string($digits)."%'";
              $result = mysql_query($query) or die('Query failed: ' . mysql_error());
    
              $search = false;
    
              //return first match
              if($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
                    $search['name']= $line['firstname']." ".$line['lastname'];
                    $search['extension']=$line['extension'];
              }
              mysql_close();
    
              return $search;
    }
    
    ?>
        

Create the Phone Interface

Next lets define the php application that handles the phone call. Here we use the Twilio Response Library to facilitate TwiML creation. We no longer have to mix PHP and XML, keep the code clean and understandable. If you haven't already, visit the Response Library introduction to familiarize yourself.

  • howtos/companydirectory/index.php
    <?php
    	
    	require "Services/Twilio.php";
        include "extensions_map.php";
    
        $receptionist_phone_number = getPhoneNumberByExtension("0");
    
        $first = true;
     
        //check if we've received an extension to try
    	if (isset($_REQUEST['Digits'])) {
        	$digits = $_REQUEST['Digits'];
        	if($digits == '*'){
        		header("Location: lookup.php");
        		exit();
        	}
    	} else {
    		$digits='';
    	}
        if(strlen($digits)){
                $first = false; 
                //look up the phone number for the extension they entered
                $phone_number = getPhoneNumberByExtension($digits);
    
                //if we found a phone number for the extension they entered
    			if($phone_number!=null){
    
    				//output TwiML to dial the extension and bridge the call
    				$r = new Services_Twilio_Twiml();
    				$r->say("Thank you, dialing now");
    				$r->dial($phone_number);
    				header ("Content-Type:text/xml");
    				print $r;
    				exit();
    			}
    
            }
    
        //If this is the first time we visit this page,
        //or the extension lookup failed, play the intro message
    
    	$r = new Services_Twilio_Twiml();
    	$g = $r->gather();
    
    	//if this is the first time this menu was hit, play a greeting
    	//otherwise explain that the extension was not found
    
    	if($first)
    		$g->say("Thank you for calling Example Incorporated.");
    	else
    		$g->say('I\'m sorry, we could not find the extension ' . $_REQUEST['Digits']);
    	$g->say(" If you know your party's extension, please enter the extension now, followed by the pound sign. To search the directory, press star. Otherwise, stay on the line for the receptionist.");
    	$r->say("Connecting you to the operator, please stay on the line.");
    	$r->dial($receptionist_phone_number);
    	
    	header ("Content-Type:text/xml");
    	print $r;
    	
    ?>    

This script is all it takes for a basic company directory. You would assign this scripts URL to your incoming phone number in Twilio. When a call is received at that phone number, Twilio makes a request to this script. Because it does not post the Digits parameter the first time, it skips the first if block and moves the the TwiML greeting block at the bottom. The greeting block begins speaking its greeting message "Thank you for calling . . ." while it waits for the caller to enter keypad digits. If the caller enters digits, Twilio will post back to this URL with the parameter Digits, containing the keypad input. If they do not enter any digits, Twilio moves on and dials the default "operator" phone number.

When this script receives a request with the Digits parameter set, it will attempt to look up the extension that matches those digits. If phone number can be found, the script returns the TwiML to Twilio instructing it to dial that phone number and complete the call. If it cannot find the extension.

Next we need to write the logic that handles Directory search and returns the results to the caller.

  • howtos/companydirectory/lookup.php
    <?php
    
    require "Services/Twilio.php";
    include "search.php";
    include "extensions_map.php";
    $error = false;
    
    if (isset($_REQUEST['Digits']))
    	$digits = $_REQUEST['Digits'];
    else
    	$digits='';
    
    if(strlen($digits)){
    	$result = search($digits);
    	if($result!=false){
    		$number = getPhoneNumberByExtension($result['extension']);
    
    		$r = new Services_Twilio_Twiml();
    		$r->say($result['name']."'s extension is ".$result['extension']." Connecting you now");
    		$r->dial($number);
    		header ("Content-Type:text/xml"); //ADD THIS TO ANY FUNCTION THAT EXITS OR XML ERROR OCCURS
    		print $r;
    		exit();
    	} else {
    		$error=true;
    	}
    }
    
    $r = new Services_Twilio_Twiml();
    
    if($error)
    	$r->say("No match found for $digits");
    
    $g = $r->gather();
    $g->say("Enter the first several digits of the last name of the party you wish to reach, followed by the pound sign");
    $r->say("I did not receive a response from you");
    $r->redirect("index.php");
    header ("Content-Type:text/xml");
    print $r;
    ?>
    
    
    
        

When the caller initially reaches this script, they are instructed to enter the first couple "letters", on their keypad, of the last name of the person they are searching for. When they submit those digits, we search the database for the record that beginning with the digits entered. Either the first match is returned or an error flag is set and the caller warned their last search was unsuccessful. If the search failed, the caller is prompted to re-enter their search again.