Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zend Framework - How to create an API that is accessible both externally and internally?

I'm looking to create a web site and will be creating a mobile app at a later date.

I want to be able to offer the same level of data (i.e. a listing of books) to both the web site and the app. I'd like to use an API for this but am struggling to find any examples or decent articles online.

So I suppose my question is, if I were to create a JSON 'endpoint' accessible by a mobile app over HTTP (e.g. http://www.mysite.com/api/v1.0/json) how do access the same functionality internally from my Zend Application?

(obviously I don't want to duplicate the database interaction 'model' steps)

like image 504
Sjwdavies Avatar asked Jan 18 '12 18:01

Sjwdavies


2 Answers

Since Zend is really not RESTful, unfortunately, your best bet is JSON-Rpc.

You can do it in a controller, or you can just make an ajax.php in addition to your index.php to reduce overhead like this guy did here

Basically, all you need to do is this:

$server = new Zend_Json_Server();
$server->setClass('My_Class_With_Public_Methods');
// I've found that a lot of clients only support 2.0
$server->getRequest()->setVersion("2.0");
if ('GET' == $_SERVER['REQUEST_METHOD']) {
    // Indicate the URL endpoint, and the JSON-RPC version used:
    $server->setTarget('/ajax.php')
           ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);

    // Grab the SMD
    $smd = $server->getServiceMap();

    // Return the SMD to the client
    header('Content-Type: application/json');
    echo $smd;
    return;
}

$server->handle();

then somewhere in your layout:

$server = new Zend_Json_Server();
$server->setClass('My_Class_With_Public_Methods');
$smd = $server->getServiceMap();
?>
<script>
$(document).ready(function() {
    rpc = jQuery.Zend.jsonrpc({
        url : <?=json_encode($this->baseUrl('/ajax'))?>
        , smd : <?=$smd?>
        , async : true
    });
});
</script>

for the sake of example, here's that class:

class My_Class_With_Public_Methods {
    /**
      * Be sure to properly phpdoc your methods,
      * the rpc clients like it when you do
      * 
      * @param float $param1
      * @param float $param2
      * @return float
      */
    public function someMethodInThatClass ($param1, $param2) {
        return $param1 + $param2;
    }
}

then you can simply call methods like so in javascript:

rpc.someMethodInThatClass(first_param, second_param, {
    // if async = true when you setup rpc,
    // then the last param is an object w/ callbacks
    'success' : function(data) {

    }
    'error' : function(data) {

    }
});

There aren't a lot of well known JSON-rpc libraries for Android / iPhone - but I have found that this works with Zend_Json_Server for Android:

http://software.dzhuvinov.com/json-rpc-2.0-base.html

and this works for iPhone:

http://www.dizzey.com/development/ios/calling-json-rpc-webservice-in-ios/

From here, obviously, you can use My_Class_With_Public_Methods in the same way that javascript / your mobile app does.

like image 66
Stephen Fuhry Avatar answered Nov 03 '22 01:11

Stephen Fuhry


From my point of view, this is more an architecture-related question than a Zend Framework question.

What you are looking for is Service-Oriented Architecture (SOA).

The premise behind SOA is simple, build a single API, and have everything go through it, whether it is internally, or externally. A popular proponent of SOA is Amazon.

In practice, it means that you expose your API exactly as you would use it internally. In OOP, this means that whenever you call your API from an external source (e.g.: a REST API), you'll specify a class name, a method name and a list of arguments, and you'll receive a object in return, just as you would if you'd call it internally.

Per example, you have these:

class HelloInstance {
    public $hello;
    public function __construct($hello) { $this->hello = $hello; }
}

class Hello {
    public function getHello() { return new HelloInstance('world'); }
}

class FooInstance {
    public $foo;
    public function __construct($foo) { $this->foo = $foo; }
}

class Foo {
    public function getFoo($value) { return new FooInstance($value); }
}

If you want to use them internally, you'd do:

$hello = new Hello;
$helloInst = $hello->getHello();

$foo = new Foo;
$fooInst = $foo->getFoo('bar');

Now you just need a gateway to expose this API externally. Here's a very basic example:

include_once 'my_classes.php';

$class = $_GET['class'];
$method = $_GET['method'];

$obj = new $class;
$return = $obj->$method(isset($_GET['value']) ? $_GET['value'] : null);

header('Content-Type: application/json');
echo json_encode($return);

You can do the same two calls I demonstrated before, and get the same results, using a REST call:

http://my_server/my_gateway.php?class=Hello&method=getHello
http://my_server/my_gateway.php?class=Foo&method=getFoo&value=bar
like image 22
netcoder Avatar answered Nov 03 '22 01:11

netcoder