zend framework1.12 rest services for web and mobile app - zend-framework

im new to zend framework and would like to create a web portal which offers a services .
The services would be used by webapplication and mobile application both.
I'm using Chris Danielson's article
http://www.chrisdanielson.com/2009/09/02/creating-a-php-rest-api-using-the-zend-framework/) as the base for.
My question is am i going in the right direction .currently im accessing the services as
http://www.zendrestexample.com/version .
1) I require it to use the url as http://www.zendrestexample.com/api/version
instead.
2)Do i need to use zend restserver for writing services ?
3)can i use the same service for both mobile app and the web app ?I mean any redirects problem arise?
4)Do i need to usezend rest client to consume those service ?
Pleas help me out..

Well you are not using bunch of PHP files to get this done... so I think you are on the right track =). The implementation in the article is okay, but very old... was written > 4 years ago. I would suggest looking into Zend_Soap_Server, Zend_Json_Server or Zend_Rest_Server. Soap solution is a bit heavy for the mobile in my opinion.
Just decide on the implementation and do little planning!
I wrote a web application and later had to add services layer in order to add mobile app interface to the application. Unfortunately this was not part of initial requirements so had to redo many things.
My advice is as follows (if you webapp and api are in a same project):
code all your application logic in library or in controller helpers. So same code can be reused in the main web application and in API layer
code your webapp logic in default module
code your api layer in a dedicated module called 'api'
phpdoc must be perfect in order for zend to autogenerate the SMD
For the API use standard JSON-RPC 2.0 protocol, there are clients for both Android / iPhone that utilize this and provide auto-discovery (SMD like WSDL but for json). All request sent via GET result in SMD being displayed all others result in handling of the request.
Utilize Zend_Json_Server for your API layer.
Here is an functional example :
<?php
// API Controller Example
class ApiController extends Zend_Controller_Action
{
public function init()
{
parent::init();
$this->getHelper('ViewRenderer')->setNoRender();
}
public function helloWorldAction()
{
$this->_handleRequest('App_Api_HelloWorld');
}
protected function _handleRequest($handlerClassName)
{
//
$this->getHelper('ViewRenderer')->setNoRender();
//
$server = new Zend_Json_Server();
$server->setClass($handlerClassName);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$cfg = Zend_Registry::get('config');
$req = $this->getRequest();
$reqUrl = $cfg->paths->basehref . $req->getControllerName() . '/' . $req->getActionName();
$server->setTarget($reqUrl)
->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);
$smd = $server->getServiceMap();
header('Content-Type: application/json');
echo $smd;
} else {
// handle request
$server->handle();
}
}
}
// HANDLER Class Example
class App_Api_HelloWorld extends App_Api_ApiHandlerAbstract
{
/**
* says "hello world"
*
* #return string
*/
public function hello()
{
return 'hello world';
}
/**
* says "hello $name"
*
* #param string $name
* #return string
*/
public function hello2($name)
{
return "hello $name";
}
/**
*
* #return string
* #throws Exception
*/
public function hello3()
{
throw new Zend_Json_Server_Exception('not allowed');
return '';
}
}
Here is sample Request (I added some bootstrap magic to pickup session by id)
https://domain.com/api/hello-world
{
"session_id": "4ggskr4fhe3lagf76b5tgaiu57",
"method": "hello2",
"params": {
"name" : "Alex"
},
"id": 123
}
Review JSON RPC 2.0 Documentation.
I found Advanced REST Client for Google Chrome to be BEST extension for developing and testing JSON web services.
For additional security you can restrict ALL the request via HTTP Auth by adding few lines of code to the abstract controller or even create security controller plugin.
Good Luck.

Related

TYPO3: Uncaught Error: Class not found any class want to use always give me this error

I am trying to build an extension. In this extension, I'm trying to connect to the database of TYPO3 but I can't access this class and any class is always not found
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class rpc {
/**
*
* #var string
*/
public $tsc_Endpoint = '';
/**
*
* #var string
*/
public $tsc_tokenID = '';
/**
* The main method of the backend module
*/
public function main(){
$connection = GeneralUtility::makeInstance(
ConnectionPool::class)
->getConnectionForTable('gpi_configurations');
$queryBuilder = $connection->createQueryBuilder();
$query = $queryBuilder
->select('*')
->from('gpi_configurations')
->where('config_name = tsc_Endpoint');
$rows = $query->execute->fetchRows();
print_r($rows);
$client = GeneralUtility::makeInstance(GpiClient::class);
try {
$server = GeneralUtility::makeInstance(JsonRpc::class)->__construct($client);
} catch (\Exception $e) {
}
echo $server->process();
}
}
$q = new rpc();
$q->main();
I want to mention something this bulk of code in a file called rpc.php
Is there a way to access TYPO3 functionality on rpc.php? I did many searches but I did not find any useful help.
installing
structure
Full edit:
From our conversation below, I think what you need is a Middleware to provide an endpoint. This endpoint reads data from the TYPO3-database passes them your your rpc-service and returns some JSON as $response.
To ensure autoloading, all your PHP-code should go into the Classes/ folder.
You have to use namespace Mazen\YourExtensionName\... in your Classes.
I would recommend to make your rpc-class a service and put it into Classes/Service/ but I might not know enough about your application.
Read the Docs about TYPO3 Doctrine, especially the part about the WHERE clause.
Reading and learning some basic priceless about TYPO3 Extension Development might be a good idea.
Middleware
https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/RequestHandling/Index.html
German, but a nice snippet for a Mailchimp endpoint: https://various.at/news/typo3-tipps-und-tricks-psr-15-mideelware-am-beispiel-mailchimp-webhook#c971
Doctrine
https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/Database/QueryBuilder/Index.html#where-andwhere-and-orwhere
Services
The Service API it self is deprecated, but you still can group some parts of your application into service ans load them with Dependency Injection from everywhere you need them.
https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/DependencyInjection/Index.html
TYPO3 Extension Development
Developing TYPO3 Extensions: https://docs.typo3.org/m/typo3/book-extbasefluid/master/en-us/Index.html

Handling POST requests with Symofny Forms in FOSRestBundle

It's not clear to me how I should use the Symfony Form Component with FOSRestBundle for POST endpoints that I use to create resources.
Here is what I've got in my POST controller action:
//GuestController.php
public function cpostAction(Request $request)
{
$data = json_decode($request->getContent(), true);
$entity = new Guest();
$form = $this->createForm(GuestType::class, $entity);
$form->submit($data);
if ($form->isValid()) {
$dm = $this->getDoctrine()->getManager();
$dm->persist($entity);
$dm->flush();
return new Response('', Response::HTTP_CREATED);
}
return $form;
}
What I do is:
Send an application/json POST request to the endpoint (/guests);
Create a form instance that binds to an entity (Guest);
Due to the fact that I'm sending JSON, I need to json_decode the request body before submitting it to the form ($form->submit($data)).
The questions I have:
Do I really always have to json_decode() the Request content manually before submitting it to a Form? Can this process be somehow automated with FosRestBundle?
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with:
-
$form->handleRequest($request)
if ($form->isValid()) {
...
}
...
I couldn't get the above to work, the form instance was never submitted.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
-
/**
* #ParamConverter("guest", converter="fos_rest.request_body")
*/
public function cpostAction(Guest $guest)
{
$violations = $this->getValidator()->validate($guest);
if ($violations->count()) {
return $this->view($violations, Codes::HTTP_BAD_REQUEST);
}
$this->persistAndFlush($guest);
return ....;
}
Thanks!
I'm trying to do the same thing to you and it's difficult to find some answers for this subject. I think today it's an important subject to develop website with api rest for mobile apps. Anyway!
Please find my answer below:
Do I really always have to json_decode() the Request content manually before submitting it to a Form?
No, i get data like this
$params = $request->query->all();
$user->setUsername($params['fos_user_registration_form']['username']);
Can this process be somehow automated with FosRestBundle?
I don't think so.
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with
Yes, but i'm currently trying to handle my form like that and I can not do it.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
No
I'm guessing if I'm not going to only register the user with:
$userManager = $this->get('fos_user.user_manager');
and make my control manually.
I posted an issue here and i'm waiting:
https://github.com/FriendsOfSymfony/FOSUserBundle/issues/2405
did you manage to get further information ?

Multiple Slim routes with the same signature

We are looking at using Slim 3 as the framework for our API. I have searched SO and the Slim docs, but cannot find an answer to the issue. If we have different route files (e.g. v1, v2, etc.) and if two routes have the same signature, an error is thrown. Is there any way to cascade the routes so that the last loaded route for a particular signature is used?
For example, say v1.php has a route for GET ("/test") and v2.php also contains this route, can we use the latest version? Even simpler would be if a file of routes contains two routes with the same signature, is there a way of the latter method being used (and no error being thrown)?
A similar question is asked here but this uses hooks (which have been removed from Slim 3 as per here)
I looked at the Slim code and I didn't find a simple way of allowing duplicated routes (preventing the exception).
The new Slim uses FastRoute as dependency. It calls FastRoute\simpleDispatcher and doesn't offer any configuration possiblity. Even if it did allow some configuration, FastRoute doesn't have any built-in option to allow duplicated routes. A custom implementation of a DataGenerator would be needed.
But following the instructions above, we can get a custom DataGenerator by passing to Slim App a custom Router which instantiates some FastRoute::Dispatcher implementation which then uses the custom DataGenerator.
First the CustomDataGenerator (let's go the easy way and do some copy and pasting from \FastRoute\RegexBasedAbstract and \FastRoute\GroupCountBased)
<?php
class CustomDataGenerator implements \FastRoute\DataGenerator {
/*
* 1. Copy over everything from the RegexBasedAbstract
* 2. Replace abstract methods with implementations from GroupCountBased
* 3. change the addStaticRoute and addVariableRoute
* to the following implementations
*/
private function addStaticRoute($httpMethod, $routeData, $handler) {
$routeStr = $routeData[0];
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
if ($route->matches($routeStr)) {
throw new BadRouteException(sprintf(
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
$routeStr, $route->regex, $httpMethod
));
}
}
}
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
unset($this->staticRoutes[$httpMethod][$routeStr]);
}
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
}
private function addVariableRoute($httpMethod, $routeData, $handler) {
list($regex, $variables) = $this->buildRegexForRoute($routeData);
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
unset($this->methodToRegexToRoutesMap[$httpMethod][$regex]);
}
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new \FastRoute\Route(
$httpMethod, $handler, $regex, $variables
);
}
}
Then the custom Router
<?php
class CustomRouter extends \Slim\Router {
protected function createDispatcher() {
return $this->dispatcher ?: \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
foreach ($this->getRoutes() as $route) {
$r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier());
}
}, [
'routeParser' => $this->routeParser,
'dataGenerator' => new CustomDataGenerator()
]);
}
}
and finally instantiate the Slim app with the custom router
<?php
$app = new \Slim\App(array(
'router' => new CustomRouter()
));
The code above, if a duplicated route is detected, removes the previous route and stores the new one.
I hope I didn't miss any simpler way of achieving this result.

action parameters routing not working in Zend framework routes.ini

I'm trying to set up a route in Zend Framework (version 1.11.11) in a routes.ini file, which would allow be to match the following url:
my.domain.com/shop/add/123
to the ShopController and addAction. However, for some reason the parameter (the number at the end) is not being recognized by my action. The PHP error I'm getting is
Warning: Missing argument 1 for ShopController::addAction(), called in...
I know I could set this up using PHP code in the bootstrap, but I want to understand how to do this type of setup in a .ini file and I'm having a hard time finding any resources that explain this. I should also point out that I'm using modules in my project. What I've come up with using various snippets found here and there online is the following:
application/config/routes.ini:
[routes]
routes.shop.route = "shop/add/:productid/*"
routes.shop.defaults.controller = shop
routes.shop.defaults.action = add
routes.shop.defaults.productid = 0
routes.shop.reqs.productid = \d+
Bootstrap.php:
...
protected function _initRoutes()
{
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/routes.ini', 'routes');
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->addConfig( $config, 'routes' );
}
...
ShopController.php
<?php
class ShopController extends Egil_Controllers_BaseController
{
public function indexAction()
{
// action body
}
public function addAction($id)
{
echo "the id: ".$id;
}
}
Any suggestions as to why this is not working? I have a feeling I'm missing something fundamental about routing in Zend through .ini files.
Apparently I'm more rusty in Zend than I thought. A few minutes after posting I realized I'm trying to access the parameter the wrong way in my controller. It should not be a parameter to addAction, instead I should access it through the request object inside the function:
correct addAction in ShopController:
public function addAction()
{
$id = $this->_request->getParam('productid');
echo "the id: ".$id;
}
I also realized I can simplify my route setup quite a bit in this case:
[routes]
routes.shop.route = "shop/:action/:productid"
routes.shop.defaults.controller = shop
routes.shop.defaults.action = index

ReSTful Facebook canvas Symfony2 application

As you may know, in order to pass user's informations(signed_request)
to your app, Facebook access canvas(ie: iframe) applications by
sending them a POST request. This mechanism is explained here.
In order to keep ReSTful, what would be the right place in Symfony (which service,
file...) to implement this Ruby on Rails' trick Pierre Olivier Martel
descibes here: http://blog.coderubik.com/2011/03/restful-facebook-canvas-app-with-ra...
, eg: convert every POST requests containing a 'signed_request'
parameter to a GET one?
You could implement a RequestListener like it is done in the RESTBundle: https://github.com/FriendsOfSymfony/RestBundle/blob/master/EventListener/RequestListener.php
Inspired from Stuck's answer(thanks!) and from Symfony Cookbook:
# src/Acme/FacebookBundle/RequestListener.php
namespace Acme\FacebookBundle;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class RequestListener
{
public function onCoreRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if ('POST' == $request->getMethod() && null !== $request->get('signed_request'))
{
$request->setMethod('GET');
}
}
}
Service Definition:
# app/config/config.yml
services:
acme.facebookbundle.listener.request:
class: Acme\FacebookBundle\RequestListener
tags:
- { name: kernel.listener, event: core.request, method: onCoreRequest }