ValiantTechnology/yelp-php

View on GitHub
src/Yelp.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php
/**
 * Yelp-API: PHP Client wrapper for Yelp's Fusion API.
 */

namespace TVW;

use Httpful;

/**
 * Class Yelp
 * PHP Client wrapper for Yelp's Fusion API
 * https://www.yelp.com/developers/documentation/v3.
 *
 * @category    API
 *
 * @author      Matthew F. Fox <mfox@thevaliantway.com>
 * @copyright   2017 Valiant Technology
 * @license     MIT
 */
class Yelp
{
    /**
     * Yelp API token.
     *
     * @var null
     */
    public $apiToken = null;

    /**
     * Yelp API Authorization bearer string.
     *
     * @var string
     */
    public $apiBearer = 'Bearer';

    /**
     * Yelp API base URI.
     *
     * @var string
     */
    public static $apiUri = 'https://api.yelp.com';

    /**
     * Default search parameters.
     *
     * @var array
     */
    private $searchDefaults = [
        'term'          => '',
        'location'      => '',
        'latitude'      => '',
        'longitude'     => '',
        'radius'        => '',
        'categories'    => '',
        'locale'        => '',
        'limit'         => 50,
        'offset'        => '',
        'sort_by'       => '',
        'price'         => '',
        'open_now'      => '',
        'open_at'       => '',
        'attributes'    => '',
    ];

    /**
     * Default transaction search parameters.
     *
     * @var array
     */
    private $transactionDefaults = [
        'latitude'      => '',
        'longitude'     => '',
        'location'      => '',
    ];

    /**
     * Default business detail/review parameters.
     *
     * @var array
     */
    private $businessDefaults = [
        'locale'        => '',
    ];

    /**
     * Default autocomplete parameters.
     *
     * @var array
     */
    private $autoCompleteDefaults = [
        'text'          => '',
        'latitude'      => '',
        'longitude'     => '',
        'locale'        => '',
    ];

    /**
     * Yelp constructor.
     *
     * @param $apiToken
     * @param string $apiBearer
     *
     * @throws \Exception
     */
    public function __construct($apiToken, $apiBearer = 'Bearer')
    {
        if (!$apiToken) {
            throw new \Exception('A token is required to continue.');
        }
        $this->apiToken = $apiToken;
        $this->apiBearer = $apiBearer;
    }

    /**
     * Request token from Yelp Fusion OAuth2 service.
     * Tokens are valid for 180 days, so caching them is good idea.
     *
     * @see https://www.yelp.com/developers/documentation/v3/authentication
     *
     * @param string $appId     Yelp App Client ID
     * @param string $appSecret Yelp App Client Secret
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public static function bearerRequest($appId, $appSecret)
    {
        $uri = self::$apiUri.'/oauth2/token';

        $body = self::urlEncoded([
            'grant_type'    => 'client_credentials',
            'client_id'     => $appId,
            'client_secret' => $appSecret,
        ], false);

        $response = self::doRequest($uri, $body, 'post');

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Returns up to 1000 businesses based on the provided search criteria.
     * An API call will return a maximum of 50 businesses - use the offset
     * parameter to retrieve businesses beyond the initial maximum of 50.
     *
     * @see https://www.yelp.com/developers/documentation/v3/business_search
     *
     * @param array $params Business search parameters
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public function searchBusiness($params)
    {
        // merge search parameter defaults with supplied options
        $keyPairs = $this->mergeParams($this->searchDefaults, $params);

        // build querystring containing only populated key/value pairs
        $queryString = self::urlEncoded($keyPairs, false);

        $uri = self::$apiUri."/v3/businesses/search?$queryString";
        $response = self::doRequest($uri, '', 'get', $this->apiBearer, $this->apiToken);

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Returns a list of businesses based on the provided phone number.
     * Accepted format: +10123456789.
     *
     * @see https://www.yelp.com/developers/documentation/v3/business_search_phone
     *
     * @param string $phone Telephone number
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public function searchPhone($phone)
    {
        if (!preg_match("/\+[0-9]{11}/", $phone)) {
            throw new \Exception("$phone is not a valid phone number.");
        }
        $queryString = "phone=$phone";
        $uri = self::$apiUri."/v3/businesses/search/phone?$queryString";
        $response = self::doRequest($uri, '', 'get', $this->apiBearer, $this->apiToken);

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Returns a list of businesses which support certain transactions.
     *
     * @see https://www.yelp.com/developers/documentation/v3/transactions_search
     *
     * @param string $transactionType Transaction type, defaults to "delivery"
     * @param array  $params          Transaction search parameters
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public function searchTransaction($transactionType, $params)
    {
        // merge search parameter defaults with supplied options
        if (!$params) {
            throw new \Exception('Latitude and Longitude are required parameters.');
        }
        $keyPairs = $this->mergeParams($this->transactionDefaults, $params);

        // build querystring containing only populated key/value pairs
        $queryString = self::urlEncoded($keyPairs, false);

        $uri = self::$apiUri."/v3/transactions/$transactionType/search?$queryString";
        $response = self::doRequest($uri, '', 'get', $this->apiBearer, $this->apiToken);

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Returns the detail information or up to 3 reviews of a business by id.
     * Business ids may be retrieved by the business, phone and transaction searches.
     *
     * @see https://www.yelp.com/developers/documentation/v3/business
     * @see https://www.yelp.com/developers/documentation/v3/business_reviews
     *
     * @param string $infoType   Determines response contents (details/review)s
     * @param string $businessId Valid Yelp business id
     * @param array  $params     Business review parameters
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public function getDetails($infoType, $businessId, $params = null)
    {
        $uri = self::$apiUri."/v3/businesses/{$businessId}";
        if ($infoType == 'reviews') {
            $uri .= '/reviews';
        }

        if (is_array($params)) {
            // merge search parameter defaults with supplied options
            $keyPairs = $this->mergeParams($this->businessDefaults, $params);
            $queryString = self::urlEncoded($keyPairs, false);
            $uri .= "?$queryString";
        }

        $response = self::doRequest($uri, '', 'get', $this->apiBearer, $this->apiToken);

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Returns autocomplete suggestions for search keywords, businesses and categories, based on supplied text parameter.
     *
     * @see https://www.yelp.com/developers/documentation/v3/autocomplete
     *
     * @param array $params Autocomplete search parameters
     *
     * @throws \Exception Contains HTTP error code returned by Httpful
     *
     * @return object Httpful response object body
     */
    public function autoComplete($params)
    {
        if (!isset($params['text']) || !isset($params['latitude']) || !isset($params['longitude'])) {
            throw new \Exception('Text, Latitude and Longitude are required parameters.');
        }
        // merge search parameter defaults with supplied options
        $keyPairs = $this->mergeParams($this->autoCompleteDefaults, $params);

        // build querystring containing only populated key/value pairs
        $queryString = self::urlEncoded($keyPairs, false);

        $uri = self::$apiUri."/v3/autocomplete?$queryString";
        $response = self::doRequest($uri, '', 'get', $this->apiBearer, $this->apiToken);

        if ($response->code !== 200) {
            throw new \Exception("API responded with a HTTP status of {$response->code}.");
        }

        return $response->body;
    }

    /**
     * Merges supplied parameters with defaults and prunes parameters that are not defined in the defaults.
     *
     * @param array $defaults default array
     * @param array $params   supplied array
     *
     * @return array merged/pruned array
     */
    private function mergeParams($defaults, $params)
    {
        $keyPairs = array_replace_recursive($defaults, $params);
        $keyPairs = array_filter(array_intersect_key($keyPairs, $defaults));

        return $keyPairs;
    }

    /**
     * Sends HTTP request via HTTPful.
     *
     * @uses nategood/httpful (http://github.com/nategood/httpful)
     *
     * @param string $uri     endpoint uri
     * @param mixed  $payload request body
     * @param string $method  request method (get, post, etc.)
     * @param string $bearer  bearer string
     * @param string $token   api token
     *
     * @return object Httpful response object
     */
    private static function doRequest($uri, $payload = null, $method = 'get', $bearer = null, $token = null)
    {
        $request = Httpful\Request::$method($uri)->contentType('form');

        // add authorization header and/or payload if necessary
        if ($bearer && $token) {
            $request->addHeader('Authorization', "$bearer $token");
        }
        if ($payload) {
            $request->body($payload);
        }

        return $request->send();
    }

    /**
     * Converts array of key-value pairs in to an application/x-www-form-urlencoded string.
     *
     * @param array $keypairs key-value pairs
     * @param bool  $encode   set to true to urlencode() string (optional)
     *
     * @return string encoded string
     */
    private static function urlEncoded($keypairs, $encode = null)
    {
        $urlEncoded = '';

        // loop through array, creating string of key/val pairs and remove first ampersand
        foreach ($keypairs as $key => $val) {
            $urlEncoded .= "&$key=$val";
        }
        $urlEncoded = ltrim($urlEncoded, '&');

        return $encode ? urlencode($urlEncoded) : $urlEncoded;
    }
}