Create Three Symfony Back Ends - Part #10 - GET one resource
5K views
Feb 14, 2023
We've done most of the hard work at this point. We have a working test suite, we can POST in new data, validate it, and save it to the database. In this video we're going to implement GET functionality for a single Album.
View Video Transcript
0:00
We've done most of the hard work at this point
0:02
We have a working test suite and we can post in new data, validate it and save it to the database
0:07
In this video, we're going to implement the get functionality for a single album
0:11
So let's quickly recap our B hat test scenario for a single album get request
0:16
So we've covered how the background step sets up our database with three albums before each scenario
0:21
and that the before scenario hook should truncate our album table to ensure that the auto-incrementing ID always resets to one
0:29
This way we can test that when we send in a get request for an album with ID1
0:34
we're always going to get back that first album. We already have our album controller
0:38
so we only need to add in a new method in this file to handle get requests
0:42
So let's start by doing just that. We're not in a new public function, which I'm going to call get
0:47
and I'm going to annotate this function with the routing annotation, setting the route to slash album slash ID, where ID is the placeholder
0:55
And I'm going to name the root get underscore album. You can of course name your roots anything that you like
1:01
but sticking with the convention of verb underscore resource seems most logical to me
1:06
Whereas for our post method, we restricted down to just the post verb
1:11
Here I'm restricting down to just get requests. Now, if you haven't worked with Symphony before
1:16
and these routing annotations are new to you, the only thing that's different between this and the post request
1:21
is that here we're using the routing placeholder of ID. And if we set this as an argument on our get method
1:27
then Symphony's going to take care of passing the. argument in for us. Now what I would always recommend is after making a routing change
1:34
just check the debug router output to ensure that your new route is in there and it also
1:39
looks the way you expect it to. Now there's one additional step that I'm going to take
1:43
As we only want integer values to be requestable, let's use a regular expression in the
1:49
requirements pattern to restrict the placeholder of ID to match just positive numbers
1:55
Now with that change in mind we could add extra tests into our symphony for Edgecase feature to check for some of these unwanted routes The tests are very straightforward We just want to check that we can send in a request for an alpha numeric ID
2:09
and also that the integer has to be positive. And at this stage, the test should be passing without further intervention on our part
2:16
Now, this is good, and Symphony's helped us quite a lot here, but we haven't actually tackled the happy path as of yet
2:23
We're just aware of some unhappy paths that we don't want. So let's get back to testing the happy path
2:29
So as the router is now helping us make sure only numeric values are making the way through to our controller code
2:35
we can now use this ID variable to look up an existing album. To accomplish this task, we'll use the album repository
2:42
And this was generated for us earlier when we used to the symphony maker bundle to make our entity
2:47
We get access to the album repository, just like we get access to any other service in a symphony four application via dependency injection
2:54
As we'll likely want access to the album repository from not just our get controller method
2:59
but from put and patch and delete to, it makes sense to inject the album repository in by the constructor
3:06
rather than just into this specific get method. So with the album repository injected and set as a private property on our controller class
3:14
we can now use it from our get method to look up the album matching the given ID
3:18
Now the album repository offers us direct access to various standard doctrine repository methods
3:23
such as find, find by, find one by, find all, and so on
3:28
We're going to use find, which takes an ID, and all being well, returns us an album with that ID
3:34
Now, you don't need to do this next step, but the local album variable is redundant here
3:38
We can inline the find call directly on the response, but this may be a little bit confusing
3:44
So if unsure, just know that these two are doing exactly the same thing, and either way should honestly be fine
3:50
Now, you could make this process even more streamlined by using a param converter to automatically convert the given ID to the matching album entity
3:58
Feel free to add this to your project There a link in the show notes So we should be able to go ahead and start testing this implementation because on the surface of it things look good at this point I just going to take the T tags off those two
4:10
tests that I don't want to run and just add that T tag to our album get request, just so we're
4:16
only running one test. And now let's send that in. And somewhat unexpectedly, this test fails
4:21
and it fails quite spectacularly, with a bit of a long-winded and rather scary-looking error message
4:27
Now, my best advice is whenever you see something like this in B-Hat, just run the test manually from Postman or whichever client that you prefer and see what you actually get
4:37
And here you can see, we're just getting back an empty object. We're actually getting that 200 status code
4:42
So things look like the semi-okay. But the issue here is just because we're returning a JSON response
4:48
Symphony won't just go ahead and JSON encode or serialize our album entity for us
4:53
Now, again, this is something that both API Platform and FOSREST bundle
4:57
will make easier for us. This just works in that case. Here, though, we need to do a little more work
5:03
Now, achieving this in PHP isn't so hard. Symphony can trigger a JSON encode on our album entity
5:09
but we need to tell it how to convert an album to Jason. And to do this, we need to implement the JSON serializable interface on our entity
5:16
And by implementing Jason serializable, we're essentially contractually obligated to implement the extra public method of Jason
5:23
serializable, which needs to return an array. and all we need to do is fill out exactly what we want to return
5:29
which in our case will be everything, but it needn't be. And notice how I'm changing the property names from camel case to snake case
5:36
Now if we send in our request again, then things look a lot better. But they're not quite perfect though yet
5:41
We need to format the date output a little bit further. So from the release date, which is a date time
5:47
I'm going to call format and pass in the format of date time atom
5:52
which is just a constant, which resolves to the string that you're seeing on screen
5:56
right now. There are others available, so please see the show notes for a direct link
6:00
And this should be enough to satisfy our B test suite at this point for a single album get request So if we switch back to our B test suite now and send in the only test with the tag of T well I was expecting that to pass honestly
6:14
But let's take a quick look at what's happened here. So we've got a value mismatch for key title
6:20
in the haystack of object. So that seems unusual. This should be working
6:24
The database should be back to its known good state. And I think that actually is the problem
6:30
We did in a previous video take out the before scenario. hook by putting a space after the at, and so our album table isn't getting truncated after each
6:39
scenario has been run. So there we go, that looks a lot better. Now there's one extra thing to
6:43
consider, and that's what happens if a resource doesn't exist. But what would happen if we
6:48
ask for, say, the album with the idea of 100, which we know definitely doesn't exist
6:53
Well, let's send that in, and somewhat unexpectedly, we're getting back a 200 OK, and perhaps we
6:58
should be getting a 404. So I'm going to add in another edge case test for this
7:03
And we know that this test is going to be failing at the moment. Now, there are likely multiple ways that we could solve this
7:09
But I know that we're going to need to look up an album by ID in more than just our get method
7:15
We're going to need to do this in both put and patch and also delete
7:20
So what I'm going to do is I'm going to extract this process out to a private function called find album by ID
7:26
Inside this function, we're going to do the same process. We're going to use the album repository to try and find the given ID
7:32
and then we're going to check if the response from that call is null
7:36
then we're going to throw a not found HTTP exception, which is going to throw a 404 in effect
7:43
However, if we do find the album, then we will just return it. This means anywhere in our controller class
7:48
where we would like to look for an album, we'll use that internal private function
7:52
rather than a direct call to the repository. So with this change made, we can send in a manual test
7:58
and we can see that we're getting back our 404 not found, and that should mean if we're sending our B
8:02
test we now have a pass which we do so that's good okay so we seem all done here onwards to
8:09
getting a collection of albums