REST API PUT request to update resource - php

I want to update one of the resources I defined (user) to update his current status (online/offline) and/or location. Therefore I would use a PUT request like this:
PUT http://server/v1/users/12345
Payload
{
"status": 0
}
to set the status offline for example.
Or it could be:
Payload
{
"latitude": 100,
"longitude": 100
}
The backend is based on Laravel/PHP and I respond to this request in my controller:
public function update(Request $request, $userReference) {
// Get payload from request
$bodyContent = json_decode($request->getContent(), true);
$userReference = $userReference;
// Update the user location
$updateResponderLocationCommand = new UpdateResponderLocationCommand($bodyContent);
$this->commandBus->execute($updateResponderLocationCommand);
$response = [
'userReference' => $userReference
];
return $this->setStatusCode(201)->respond($response);
}
This controller uses a command system I integrated that will trigger the task of doing the update.
My questions are, where I struggle:
How do I do differentiate between the commands that should be executed. Right now, only the LocationUpdate is in this method. But I don't want to write a new update message just for the status update.
So how does a request need to work properly? Can I still use the approach with PUT http://server/v1/user/100 and by having keywords in the payload and a select switch - approach within the controller a differentiation between tasks to be executed upon?
Am I suppsoed to use PUT at all, when I only update a single component? I read that I should use POST instead?

Typically, the corresponding action would be sent through the routing mechanism. We would have an action property on your json object that corresponds to the function that should take place.
Lets have a look at that:
public function update(....){
switch($request->get('action'))
{
case 'location':
//execute the command here
break;
case 'status':
//execute the command here
break;
}
}
In this way, we can make sure that our commands are dispatched according to our actions. This is the method that I prefer.
Regarding the whole put v.s. post - there's no reason to do another write-up as this question thread here has the best breakdown and explanation by far.
You should refer back to the link in #2. When creating a resource, you should use PUT. This makes sure that the request is idempotent and will not be duplicated (client's hung, server bottlenecked, etc).

Related

Laravel - check request method

I'm an iOS lead on an app and trying to fix some API bugs whilst our dev is 'unavailable'. I'm almost completely new to Laravel and trying to check what the request method is. I have followed some guidance from another question but have been unable to get it working:
public function defaults(Request $request, User $user){
$follow_ids = explode(',', env('FOLLOW_DEFAULTS'));
if ($request->isMethod('post')) {
return ['user' => $user];
}
$user->follows()->syncWithoutDetaching($follow_ids);
return ['user.follows' => $user->follows->toArray()];
}
Do you know where I might be going wrong here? Thanks in advance.
When the request is returned it always just seems to skip over and return ['user.follows' => $user->follows->toArray()]
$request should be an instance of Illuminate\Http\Request. This class extends Symfony's request (Symfony\Component\HttpFoundation\Request), which is actually where the isMethod() method is defined.
Basically, given the function definition as posted, it reads "if this is a POST request, just return the user data. if this is not a POST request (e.g. GET), update and return the relationship data."
So, if you send a POST request, you'll get the ['user' => $user] response. If you send any other request method (e.g. GET), you'll modify the follows relationship and get the ['user.follows' => $user->follows->toArray()] response.
To me, this seems backwards. I would think you'd want the POST request to update the data, and any other request (e.g. GET) to just return data.
If this is correct, you need to negate your isMethod check:
if (! $request->isMethod('post')) {
return ['user' => $user];
}
More appropriately you should define separate controller actions to handle POST vs GET requests, but that is outside the scope of this question, and probably more than you want to get into as a temporary maintainer.
It seems that the request is not a POST so the if check is never true. You could echo the method name like this:
$method = $request->method();
echo $method;
// or var_dump($method);

Laravel API patch method

I am developing a laravel api and sucessfully made gets, posts, deletes and i am now trying to do the update method using PATCH.
Here is my first try:
public function update($id, Request $request)
{
$taxes = taxes::find($id);
$taxes ->fill($request->only(['$request']));
$taxes->save();
}
And testing it with the follow url on postman
****.local/api/taxes/1?description=test
This obviously doesnt work, i tought i would be able to see the error but i am currently on a different machine but hopefully someone can guide me to correct path to make this update method.
Dont forget its an api, no views/forms.
Thank you.
You have to specify the body of the query (so the variables you want to change) in the x-www-form-urlencoded tab of the Body tab for your request in Postman.
You can find a screenshot here: http://img11.hostingpics.net/pics/987644Sanstitre1.jpg (waiting for imgur to be back online)
You also have an issue in your method, your code should be the following:
public function update($id, Request $request)
{
$taxes = taxes::find($id);
$taxes ->fill($request->only(['description', 'anyotherfield', '...']));
$taxes->save();
}
Could be to do with this line: $request->only(['$request']), as your request is unlikely to have a parameter called $request. You might've been meaning to use $request->all() (all input) or $request->only('description', 'etc.') which will whitelist given paramters.
Probably should give your model a singular name, and capitalize it.
Tax::findOrFail($id)->update($request->only('description'))

How to handle eager loading on REST API

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.

How to get data back from a command bus?

I'm fairly new to domain driven design concepts and I've run into a problem with returning proper responses in an API while using a command bus with commands and command handlers for the domain logic.
Let's say we’re building an application with a domain driven design approach. We have a back end and front end portion. The back end has all of our domain logic with an exposed API. The front end uses the API to make requests to the application.
We're building our domain logic with commands and command handlers mapped to a command bus. Under our Domain directory we have a command for creating a post resource called CreatePostCommand. It's mapped to its handler CreatePostCommandHandler via the command bus.
final class CreatePostCommand
{
private $title;
private $content;
public function __construct(string $title, string $content)
{
$this->title = $title;
$this->content= $content;
}
public function getTitle() : string
{
return $this->title;
}
public function getContent() : string
{
return $this->content;
}
}
final class CreatePostCommandHandler
{
private $postRepository;
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function handle(Command $command)
{
$post = new Post($command->getTitle(), $command->getContent());
$this->postRepository->save($post);
}
}
In our API we have an endpoint for creating a post. This is routed the createPost method in a PostController under our Application directory.
final class PostController
{
private $commandBus;
public function __construct(CommandBus $commandBus)
{
$this->commandBus = $commandBus;
}
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->commandBus->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
}
Now in our createPost method we want to return the data of our newly created post in our response object so our front end application can know about the newly created resource. This is troublesome since we know that by definition the command bus should not return any data. So now we're stuck in a confusing position where we don't know how to add our new post to the response object.
I'm not sure how to proceed with this problem from here, several questions come to mind:
Is there an elegant way to return the post's data in the response?
Am I incorrectly implementing the Command/CommandHandler/CommandBus pattern?
Is this simply just the wrong use case for the Command/CommandHandler/CommandBus pattern?
First, notice that if we wire the controller directly to the command handler, we face a similar problem:
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
The bus is introducing a layer of indirection, allowing you to decouple the controller from the event handler, but the problem you are running into is more fundamental.
I'm not sure how to proceed with this problem from here
TL;DR - tell the domain what identifiers to use, rather than asking the domain what identifier was used.
public function createPost($req, $resp)
{
// TADA
$command = new CreatePostCommand($req->getPostId()
, $command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// happy path: redirect the client to the correct url
$this->redirectTo($resp, $postId)
}
In short, the client, rather than the domain model or the persistence layer, owns the responsibility of generating the id of the new entity. The application component can read the identifier in the command itself, and use that to coordinate the next state transition.
The application, in this implementation, is simply translating the message from the DTO representation to the domain representation.
An alternative implementation uses the command identifier, and derives from that command the identities that will be used
$command = new CreatePostCommand(
$this->createPostId($req->getMessageId())
, $command->getTitle(), $command->getContent());
Named UUIDs are a common choice in the latter case; they are deterministic, and have small collision probabilities.
Now, that answer is something of a cheat -- we've really only demonstrated that we don't need a result from the command handler in this case.
In general, we would prefer to have one; Post/Redirect/Get is a good idiom to use for updating the domain model, but when the client gets the resource, we want to make sure they are getting a version that includes the edits they just made.
If your reads and writes are using the same book of record, this isn't a problem -- whatever you read is always the most recent version available.
However, cqrs is a common architectural pattern in domain driven design, in which case the write model (handling the post) will redirect to the read model -- which is usually publishing stale data. So you may want to include a minimum version in the get request, so that the handler knows to refresh its stale cache.
Is there an elegant way to return the post's data in the response?
There's an example in the code sample you provided with your question:
public function createPost($req, $resp)
Think about it: $req is a representation of the http request message, which is roughly analogous to your command, and $resp is essentially a handle to a data structure that you can write your result into.
In other words, pass a callback or a result handle with your command, and let the command handler fill in the details.
Of course, that depends on your bus supporting callbacks; not guaranteed.
Another possibility, which doesn't require changing the signature of your command handler, is to arrange that the controller subscribes to events published by the command handler. You coordinate a correlation id between the command and the event, and use that to pull up the result event that you need.
The specifics don't matter very much -- the event generated when processing the command could be written to a message bus, or copied into a mailbox, or....
I am using this approach and I am returning command results. However, this is a solution which works only if the command handlers are part of the same process. Basically, I'm using a mediator, the controller and the command handler get an instance of it (usually as a constructor dependency).
Pseudo code controller
var cmd= new MyCommand();
var listener=mediator.GetListener(cmd.Id);
bus.Send(cmd);
//wait until we get a result or timeout
var result=listener.Wait();
return result;
Pseudo code command handler function
var result= new CommandResult();
add some data here
mediator.Add(result,cmd.Id);
That's how you get immediate feedback. However, this shouldn't be used to implement a business process.
Btw, this has nothing to do with DDD, it's basically a message driven CQS approach which can be and it is used in a DDD app.

Best Practices and how to find get POST data from iOS AFNetworking in Symfony2 and return JSON in GET?

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.

Categories