Currently, I am returning data like so (basic example):
public function index()
{
return User::all();
}
However, I want to wrap my responses with some extra meta data, so that they look something like:
{
'success': true,
'data': {
... // Normal response
}
}
The success value would be something as simple as statusCode == 200.
I've read about using response macros, response factories, after middlewares, the Fractal library etc
Ideally it will work with all responses, e.g. returning Eloquent Models and Collections, as well as with Response::json($data).
What is the best / right way, at the time of Laravel 5.2, to achieve this?
Thanks.
If you're developing a API for Laravel, I'd recommend checking out Dingo. It is one of the most useful package for developing APIs. Dingo uses Fractal to transform the responses. In your application to add such metadata, you could use Transformers in Dingo. There are also a lot of events that you can listen to and modify your data. In your particular example, ResponseWasMorphed would be one of the event that you would listen to.
If you want to continue with your project without adding external package, you can override the response() method in your BaseController which could check and add the needed.
public function response($data, $status)
{
//calculate parameter based on $status
$success = ...
return response(array_merge($data, ['success' => $success]));
}
You can use the fractal package for that. Then you can run the response through a transformer and get the proper response. Here's the package for that. https://packagist.org/packages/league/fractal
Related
Imagine you have the following resources for example: Users, Posts and Comments (With typical relationship setup in laravel).
When fetching a single Post, you will have the following endpoint
GET /api/posts/1
// With route model binding
public function show(Post $post)
{
return $post;
}
This is fine if I only want the Post object, but in some pages in my application, I also need to load the User and the Comments associated with the Post.
How do you guys handle that kind of scenario?
1. Should I load everything in that endpoint like:
return $post->load(['user', 'comments.user']);
and call it a day? (nope)
2. Should I accept an additional parameter that will tell my controller to load the relationship based on that value?
// With route model binding
public function show(Request $request, Post $post)
{
// rel for "relationship"
if ($request->has('rel')) {
$post->load($request->input('rel'));
}
return $post;
}
with this approach I could do something like this:
GET /api/posts/1?rel=user
returns Post with User
or I could build an array of parameter with jquery's $.param(['user', 'comments.user'])
GET /api/posts/1?rel%5B%5D=user&rel%5B%5D=comments.user
returns Post with User + Comments.User
but anyone can easily mess with the 'rel' parameter so I also need to check that
¯\(°_o)/¯
3. Just create a new endpoint for every specific requirements. (what should your endpoint look like for the example above?).
I'm building a SPA with Angular + Laravel (just a self-consumed API) for my Internal Project when I encounter this pitfall. The second approach is what I currently using for basic fetching and I use the third approach for more complex requirements.
Any inputs are appreciated.
I have a controller for ProductController. I have 4 standard methods bound to respective HTTP methodslike
public function index() // GET
public function create() // POST
public function update() // PUT
public function destroy() //DELETE
So far so good, but i have to make few other functions like getProductsByCategory, getProductsAttributes() etc etc. After implementing this, Will my API still be called REST ? If not than how can i handle these requirements
Thanks
Resource URI for getProductsByCategory(...):
GET /products?category=books HTTP/1.1
Host: service.org
Resource URI for getProductsAttributes():
GET /products/bmw-528i/attributes HTTP/1.1
Host: service.org
How you implement handling of these request URIs is a implementation detail. If you are using some framework, you can do something like this:
Routes::add("/products/{product-id}/attributes", function($request, $response) {
// do something here
});
But it is a detail that can not affect RESTfullness of your service.
First off, REST is not a strict standard. The methods you posted comply the REST conventions but a REST service must have several other properties. The two most important ones are:
statelessness: no session, no cookies, authorization on a per-request basis
GET requeste never change any resource
There are other ones, feel free to edit or add in the comments.
The way i see such operations on resources implemented most of the time is:
/<resource-name>/<product-id>/<operation>
For example:
GET /product/<product-id>
GET /product/<product-id>/related
POST /product/<product-id>/purchase
GET /categories/tools
I have an API controller, where I always return JSON. Returned status will always be 200, because for other cases I'm throwing exceptions and handle them globally. I used to return response()->json($content); in each controller's method, but I changed it to form JSON in the middleware. Now, I'm having return response($content); everywhere. I was thinking about simplifying it even more and just return $content; instead. It works, but I am not sure if it's reasonable solution.
Are there any traps behind this idea?
I believe this is a good solution since you are using a middleware. In routes you don't want to return data as JSON you can simply exclude them from running your middleware.
In the future if you want to return your data in another format you can even pass the format as an argument when running the middleware. For example:
Route::get('resource/{id}', ['middleware' => 'format:xml', function ($id) {
//
}]);
I am building a mobile app (iOS) and Symfony2 REST backend. On Symfony2, my routes are working correctly and I have tested them with AJAX and httpie, all CRUD operations, etc are fine. Now, I am trying to access the routes from the app. So far, I can access the routes and when I look into the Symfony2 Profiler, I can see entries in last 10 entries to verify that I am hitting the server with my POST and GET requests. Now, I have 2 questions and I would be glad if people can point me in the direction for ** Best Practices ** on how to proceed.
Problem 1: Although I am posting data which I can see coming in under "Request", when I try to create a record, it creates only NULL records, meaning the data is being lost. This is my controller for creating users for example:
public function postUserAction(Request $request)
{
$content = $this->get('request')->getContent();
$serializer = $this->get('jms_serializer');
$entity = $serializer->deserialize($content, 'Name\BundleName\Entity\User', 'json');
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return array(
'entity' => $entity,
);
}
When I look into the log, the only things that stand out are: Request Cookies (No cookies), Request Content: "Request content not available (it was retrieved as a resource)." This tells me the data was missing, how can I get this data and use it? Or what else could it be?
Problem 2: GET returns an empty JSON response with no data just the keys when I NSlog (echo it). My code looks like:
public function getUsersAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('NameBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
From the log, it has the Request Cookies set: PHPSESSID => "1udududjjs83883jdlb4ho0j4" but again the Request Content says: "Request content not available (it was retrieved as a resource)." How can I make it return the data with the JSON? This works well in the browser AJAX and httpie tests.
Problem 3: Using AFNetworking, I have a symbolic constant which I set as the APIHost (IP Address) and APIPath was the folder. Now in my earlier version using native PHP, I constructed the actual code to be executed in index.php by sending the parameter in JSON so if I wanted a login, I sent something like todo:login but with Symfony2, I am not sure or know even the best practices for this case. Ideally, I would like to specify the server-side request in the JSON request and then find the correct route in Symfony2 but is this how to do it and if yes, can you please provide an example? The workaround is to specify hard coded paths in AFNetworking each time I need to make a request which I think tightly couples the code and I need to make changes in a lot of places anytime something changes on the server side. Thanks and sorry for the long question!
You expect the jmsserializer to do magic for you. But it won't, you have to configure it first. From you code I can see that you are using jmsserializer wrong.
In getUsersAction() you have to return a serialized response, but you are returning an array of objects. This would be the right way:
public function getUsersAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('NameBundle:User')->findAll();
$serializer = $container->get('jms_serializer');
return array(
'users' => $jsonContent = $serializer->serialize($entities, 'json');,
);
}
Your post action basically looks ok, however when the json does not contain every field of entity USER the deserialization will fail. You can configure the entity for serialization/deserialization using annotations.
http://jmsyst.com/libs/serializer/master/reference/annotations
I am not sure if I understood your last problem, but I think you have to hardcode the path in your app.
Symfony2 is great and absolutely useful when writing an API. But if you don't want to deal with serialization/deserialization you can give http://laravel.com/ a try. It is build on symfony and you can generate an api on the fly.
Can someone point me in the proper direction for creating a zend-mvc style postDispatch plugin?
I tried using afterDispatchLoop() but I need a way to access the current response. The goal is to do some sort of filter on the Response (rendered layout + view) , and then reset the response with the new filtered text.
I also tried using afterRender() and beforeRender() but had no luck because the response that I get from the dependency injector is empty.
Please help if you know how to achieve this! Thank you!
UPDATE
I have got it working using the View event (afterRender). This event passes an instance of the Phalcon MVC view, where you can subsequently modify the body of the entire view but calling $view->getContent() + manipulation, followed by $view->setContent().
I am used to working with HTTP Response objects like in Zend. Is there a better way to achieve this sort of response manipulation in Phalcon?
if you get empty results from services in dependency injector, you can enable php singleton design pattern for services with just adding "true" flag like this:
$di->set('view', function() use($config) {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir($config->application->viewsDir);
$view->registerEngines(array(
".volt" => 'volt',
));
return $view;
}, true);
$di->set('service', function(){}, SINGLETON_TRUE_OR_FALSE)
more info here: http://docs.phalconphp.com/en/latest/reference/di.html#shared-services