This screencast uses Yii 1.1.14. You can grab the code from https://github.com/mattmccormick/screencast-yiiframework-restfulapi
Hi, there. My name is Matt McCormick. Welcome to this screencast on building a RESTful API with Yii Framework. It’s my first one, so I hope you’ll enjoy it and get a lot out of it. I’ve used Yii for a couple years and it’s a pretty good framework to build things quickly. And I enjoy working with it.
In the past, I’ve developed a RESTful API with Yii and it’s a little tricky, so I thought I’d present this screen cast to you to see what you can do to get things set up. All right, so let’s get started. We’ll start right from the beginning.
So, the first thing to do is set up a Yii Project. We’re going to use Composer, which is a package manager for PHP. So, if you go make up a project directory; let’s call this RESTful API. Let’s make that first, RESTful API and go into that directory. If we create a composer.json file. And we’ll set things up… instead of installing the vendor packages in the route to vendor directory, we’ll use Yii’s standard, which is under the protected folder.
And we will require… Yii requires PHP, at least 5.1. And Yiisoft/yii is the location for the Yii project on Packagist. So, that’s our composer file. So, if you’re wondering where I got that from, if you go to Packagist, then search for Yii. So, Packagist.org.
You can see here, if we use the latest version, it will tell you what commands you need to put in. Actually, Yii already requires PHP 5.1, so we definitely need to put that in. But we’ll just leave it for now.
So, if you haven’t used Composer before, definitely check it out because it makes things a lot easier to get started with. So, we’ll save this file, here we have it. And I’ll do Composer install. Looks like I didn’t type something correctly. Should probably check the error message first, line three after vendor. Maybe it’s the key, which is the vendor directory.
All right, let’s try that again. So, if you haven’t used Composer before, definitely check it out at GetComposer.org. It’s pretty easy to work with and once you start using it, you’ll definitely not want to go back to the downloading packages and putting them in your own directories directly. So, that’s installed. If we look at Protected Vendor Directory, we see that Yii is there with these other files where Composer sets up.
So, now, we can install Yii. So, the command to install Yii is Yii contains YIIC command, which is like the command line to run commands. And we want to install the web app and in this directory. So, access to make sure, create a web application under this directory, guess we’ll do that. There we go.
So, now you see, before, there was just the protected directory and the Composer file. Now, it’s placed the assets, CSS, public-facing files and protected directory. The protected directory is where all your controllers and models and code will live.
So, we can start this up. If we run a PHP server here, it should start up directly and there we go. Our application is already running. The default application that we made using the command line will create a few pages and also set up a log-in form as well.
All right, so now, let’s look at what was set up. We’ll open up my ID here. So, I use PhpStorm, but it doesn’t really matter what you use. So, we’ll create a new project from existing files. One server is installed locally. And this is under my home directory and RESTful API project route.
We’ll add a new server and port 8000 and this is RESTful API. So, PhpStorm has support for Yii framework, which helps with accessing some classes and methods.
So, you can see most of the stuff is under the protected directory. You have your controllers, the site controller. You have models set up. And you have your views. The vendor directory, there’s Yii there. And that’s what we’ll need basically for now.
So, one thing that Yii comes with is this co-generation module called Gii. And if we open up the config file, it’s under your configuration folder, there’s a main PHP. And under “Modules,” it says “Uncomment the following to enable the Gii tool.” So, we’ll uncomment that so we can use it.
Now, if we go back to our application and go to index.php?r=gii
, we’ll be able to access it. And the password is just enter your password here, that’s in the configuration file. So, we enter that. Now, we have access to the co-generator.
So, the first thing we’ll want to do is create a model. This is just going to be a simple example, so we’ll just have one model and one controller. So, just for the sake of it, let’s call the table “Frogs.” The model class will be, I like singular model classes, so just call it “Frog.” And we go to “Preview” and table frogs does not exist.
So, the first thing is, we need to create the table. If you go back to your directory and you run the E-Command again, you’ll notice before we ran the command under “vendor/bin/yiic
,” after we installed Yii, there’s a command just under the protected directory, so you can use that. So, we’ll do protected/yiic
and let’s see what happens.
So, these are the commands that come by default with Yii. So, we used webapp before to create the web app. And we are going to be concerned here with migrate. If you want, you can look up the others, but we’re not going to be using it in this demo.
So, if we do YIIC migrate, it’ll say it created our migration history and no new migrations found. So, let’s just see what this does.
So, this command provides support for database migrations. So, a database migration will allow us to change or alter the database. And the reason why it’s good to use a database migration tool, is that you’ll have a record of all the changes. So, if someone new comes onto the project, they just have to run this command and they’ll instantly be set up with their own database. So, it makes things much easier to track, especially once the project grows. You can have a history of what was done.
If you were to modify the database directly and just kind of update that, you wouldn’t have the history. Which can be important later on.
So, we will create a migration here, so the command is YIIC migrate create and then the name you want to call it. So, will say “create_frogs_table.” Yes to that. So, you can see, it placed it under the protected migrations folder and it gave it the timestamp as well.
So, if we go to the migrations folder, you’ll see it’s right here. So, now, let’s go about creating our table. Whoops, we switched to Japanese there. So, we want to go with this create table. We’ll call this the “Frogs” table and we add an array of what we want to store in it.
So, Yii provides a couple of shortcuts. We can just say “id => pk
” which is the primary key. You’ll add a name, which is a string and say that it’s required. And the color, which will also be a string, but is not required.
And in the down migration, we will do the reverse. So, when you’re creating a migration, it’s important to put both parts. Because if ever you, later on, if you deploy a command and you find it broke something, it provides an easy way for you to roll back the database to the previous step.
So, going back to our command line, we can now try running yiic/migrate. So, if we do migrate, it will say “Do you want to apply this migration? Does it want to be applied?” Yes, we do.
So, let’s check it out. By default, if we go back to our configuration file, Yii, under “Components,” here is the database that Yii is using. So, you can see the connection string is referring to an SQLite database. Under the data test drive, under the data folder called “testdrive.tb.” So, you can see that here.
So, if we opened up with SQLite database browser… you can see that the frogs table has been created. If you want, underneath, it says you can uncomment to use a MySQL database, but SQLite is fine for such a simple thing to get going. And usually, I like to start out by using SQLite because it’s so simple. And then, once you run into its limitations, then I usually switch to MySQL. Because SQLite is definitely the most simple database out there.
Okay, so we’ve created the table. Now, we can back to our model generator and build the class. So, if we preview it again, it says it’s going to generate a models/Frog.php file. And there we go. Let’s see how it worked.
So, here, under our models, we see that frog has shown up. And there’s been a lot of code added already by Yii. So, we have the table name specified, the rules. So, these are… Yii is going to use these for validation. When the user enters a name, it’s going to check if it’s required and for name and color if it’s less than 255 characters. If there’s any relations, like to other tables, they will appear here. We’re not going to be using this in this example and some other things, which we won’t be concerned with.
So, we have our model now. Let’s create our controller. So, we’ll use the Crud Generator because we want to have all the different actions available. Create, update, delete and view.
So, our model class that we just entered is Frog. And the controller ID will be called “Frog” as well. Let’s preview that and here’s all the files. It’s going to generate a controller and then a bunch of views. Here we go and they show up; here’s our Frog controller.
So, if we go look at our app here, if we go to index.php slash and put the… so, slash frog doesn’t work yet because we’ll need to enable pretty URLs first. But we’ll come back to that later.
So, if we go to r=frog, we can see that we have the frog’s index page show up here and there’s no results. So, now, we can go about starting to create… instead of using the standard methods, we will go about creating them via Ajax instead.
So, one of the first things notice is with Yii, there’s a “filters()
” method. This will perform checks before running the action to make sure that the correct request is being made. So, you can see, it’s put an example here already. It is going to run access control, which is all the rules that are under “accessRules()
.” And it’s going to make sure that post, that the delete action is only run via post. So, this is important because you don’t want to have a get request accidentally perform a delete action. Because that means anytime Google is scraping your website and hits that link, it could potentially delete something.
So, these are a couple examples to get started. If we want to create true RESTful API, we will change… “postOnly
” will be “create.” We will use
“PUT” for “update.” And we will use a DELETE method for “delete.”
So, these access rules are the way Yii handles… making sure the user is authorized to perform the action. So, we’re not going to bother with these now. But to just give you a quick rundown, it says here… it’s basically saying for the index and view actions, all users are allowed to perform them. For the create and update actions, authenticated users are allowed. And for the admin and delete actions, only admin users are allowed. And if anyone does not fit into these categories, they are denied access to the actions. But we’re not going to worry about that for now for this app, so we’ll just remove this.
Okay, so if we go to our index action, you can see that Yii has… they call it “ActiveDataProvider,” which will perform a search and return the results. So, you see here, when we went to r=frog, this is the action that is being called.
However, this is not going to be performed by Ajax because this render will render the view. So, under “views/frog/index” you can see that here, the data provider… a Yii list view widget will be used to render the results.
So, the first step is to change this into an Ajax call. So, we’ll keep the data provider for now. And instead of setting it to the view, we will want to output it via Ajax. So, to make sure that the browser interprets the response correctly, we will it to accept Json. That it will be a Json response. And we will give it a 200 status code for success. And then, we will echo.
So, here, we are just going to put out Json. And we’re going to put it in a data key. And we’ll just spit out the data here.
And then, Yii has this method here to finish, to shut down the application. So, it’s better to use this than just exit because it allows Yii to kind of do any shutdown methods that are needed.
So, let’s take a look at that. If we go back here and refresh, we see that now, we have Json coming out. And we’re on our way, it’s the first step.
So, let’s move onto the create action. We can get the results, but it’s pretty boring until we can start creating some and seeing how they change.
So, here, we have our create action. Now, it’s kind of good to understand how Yii sets things up. So, it’s creating a new frog here. It’s checking if this is a post. It’s setting the attributes to what the user submitted. It’s saving them and then, it’s doing the post redirect; I forget the technical name.
But basically, when you submit a form, you generally want the user to be redirected somewhere. Because that prevents them from seeing that method, the message that “Do you want to resubmit your form?” Whenever they click the back button.
So, we’re going to change this a little bit to perform via Ajax. So, we will set the attributes to post, whatever’s posted. And we will save the model.
And we want to send the Ajax response. So, one thing, instead of duplicating this code here, we know we’re probably going to be using it a lot, since all our responses are going to be via Ajax. So, let’s take a moment to re-factor this out into a method that can be used throughout the application.
Basically, whenever you notice “Hey, I want to use that same code again,” it should be a hint for you to re-factor and take out this code, so that you can use it again in your app without needing to copy and paste. Copy and paste should be used very minimally when you’re developing software.
So, what we want to do is let’s call it something, “sendAjaxResponse()
.”
And we are going to pass the model into it.
So, now, we need to think of a way… let’s take this out, put it here.
“private function sendAjaxResponse()
” and we have the model.
So, the first thing we notice is that one action, the index action, is going to be sending a “CActiveDataProvider” object. Whereas the “Create” action, we want to be sending a Frog object.
So, we need to find a way to make this method kind of accept both of these objects. So, the way to do that is by creating an interface, which allow us to create common methods. So, we know that any object that fits this interface, we will be able to access standard methods. And then, it will make things much easier.
So, we’ll just put this under “Components” for now to make it easy. I’ll call it “AjaxResponseInterface.” So, Ajax…
And basically, we want to have two methods available. We want to have a “toJson()
” method. And we want to have… sorry, we want to have a data method. Because Json will be converted in the controller. And we want to have a “getErrors()” method.
So, if we go back here, if we make sure that Ajax response interface object or an object that implements Ajax response interface is passed in, then what we can do is we can have here, “$model->getData()
” and we can also notify the user of any errors by “$model->getErrors().”
So, the first question is “How do I make sure this data provider is implementing Ajax response interface?” So, we’ll need to create a child class that extends from “CActiveDataProvider.”
So, under “Components,” we’ll create a new class called just “ActiveDataProvider.” And we’ll extend CActiveDataProvider and we will implement AjaxResponseInterface.
So, now, we need to implement those methods. So, CActiveDataProvider already has a “getData()” method. So, we will leave that for now and see how it works. We need to implement a “getErrors()” method. And this is just doing a search, so really, there shouldn’t be any errors. So, we’ll just return an empty array.
And we’ll need to change this to just “ActiveDataProvider.” So, it can be passed in correctly. Let’s see how that works.
If we go back to our index action, there we go. Now we see that we have data and errors and they’re both empty. And going back to “Create,” now we have to make sure that that frog implements the Ajax response interface as well.
So, if we open up our frog model and we implement the interface, we have to add a “getData()
” and “getErrors()
” method. Actually, I believe active record already has “getErrors()
” available. Yes, it does. “getErrors()
” is under CModel. So, we don’t need to do that. We’ll just leave it as is.
But forget data, we want to return the… Yii calls it “attributes
.” So, that’s the data which will be like the ID, name and color.
Okay, so let’s give this a try. Let’s go to our command line. If we use curl, we want to create a post request. We are going to pass in name=Kermit. And color is… he’s a green frog. We will make this to “localhost:8000/index.php?r=frog/create
”. There we go. It looks like we got the correct response. So, we got back data, our name, Kermit, color, green and the ID of one. And empty errors.
Now, remember how name was required? So, what happens if we try to do this request without name? So, we get the error back. Name cannot be blank. So, this is working so far. Let’s create another frog as well, just because we have extra data to work with. We’ll create “Hypnotoad” and he will be brown.
So, let’s go back to our index and see… we see that the data is showing up that there’s two. But we’re not seeing the actual data. So, there’s something that needs to be changed with that. So, back to our active data provider. What we’ll have to do is overwrite the data. But we can’t do that because unless you’re providing the data in the standard form… if C Active Data Provider happens to be used anywhere else in the app, they will be expecting data in a specific format.
So, what we need to do is create… call it something different. So, let’s call it “getResponseData()
.” That means we need to change it in the interface as well. And we need to change it in the frog controller as well. And we need to change it in the frog controller.
So, how does this work? Basically, it looks like… let’s take a look at what the data is, first of all. So, we see we have a frog object. And under the attributes is where the data is stored. So, what we’ll have to do is kind of loop through those attributes and get them.
So, let’s create an array; I’m using PHP 5.4, array syntax. If you’re running on PHP 5.3, I believe, or lower, you will need to do the old array syntax. The short syntax won’t work. It will loop through getData() as $record. It will push that into the result array. And enter the result.
So, now, let’s take a look here. And we see that the data is showing up as we would expect. So, to just go over again what I did there is that when we had the active data provider object, the data comes from the attributes, not the “getData()” array. So, by looping through each frog object and getting the attributes, we use that to set it to the response data.
Okay, so we have the index working and we have the create working. Now, let’s get the update working. Actually, let’s get the view working first because we want to be able to view an individual frog.
So, this one is pretty simple. Instead of rendering it, we are just going to send the Ajax response and load the model of the ID. So, when you created the controller via Gii, it put this load model method in, which is kind of a shortcut. It will find the frog with the primary key of ID and return it. Or if not, it will throw an exception.
So, basically, we can just have one line to send it. So, if we now go to “frog/view/id/1
,” there we go. We’re already getting the data back. And we can view the second one, as well.
All right, so that’s index, create and view are working. Now, move on to the update. This will be kind of the same as “Create,” except instead of creating a new model, we are loading the existing model. And then, we will set the attributes to… now, we can’t use post here because we want to implement the true RESTful API and use the put method.
So, Yii has a different way of accessing the put requests. If we… under the “request object,” there’s a “getRestParams()
.” And this will read in from PHP input and return them correctly to us.
If we tried it by post, we would notice that post was just empty. So, then, we’ll save it. You can try that if you want. You can try and do a post and see what happens. Actually, why not do that? We will print our post and see what happens. Model save. So, this is basically following from here, model attributes. It said it, it saved it and redirected. And then, we send the response in.
Okay, so, now, let’s give this a try. So, first, let’s try it by POST. Let’s say we are going to update Kermit’s color to blue. Okay, so you see this didn’t work here. So, here we try to post requests. And if you can see here, “filterPutOnly()
” is invalid. So, controller, Frog controller, does not have that filter method, put-only.
So, Yii comes with a “postOnly
” filter, but it doesn’t come with put-only or delete-only. So, that’s something we’ll have to implement ourselves.
So, if we go to the controller class, we’ll put it in the controller because this is probably something you’ll want to use throughout your app. And we create “Filter Put Only Filter Chain.”
So, if you look at “CController,” enter “postOnly
.” Here’s the different filters that Yii has. So, for example, they have “postOnly
.” Checks for Ajax-only and then, the access control filter for the rules. So, basically, we’ll want to copy this and change it to “put and delete.” So, then, we can use it within our app.
So, “putOnly
” and instead of checking if it’s a post request, we’ll want to check “isPutReqeust()
.” And let’s do that for “Delete” as well. Filter delete-only. Delete.
So, now, if we run it again, we see we got another error. Let’s see. “Your request is invalid.” Unfortunately, this error from Yii is not very helpful. So, sometimes, if you’re debugging the Yii app and you’re getting “Your request is invalid,” it usually means it’s because of the filters.
Now, one of the things they could improve is by putting more information for developers to know about that. But after you’ve experienced it a couple dozen times, you’ll start to figure it out.
So, we did a post request. If we change that to “PUT,” let’s see what happens. It works correctly. So, this array was us… where was it? We had to print our response, there it is. This was seen if there was any post data. And no, it isn’t. So, that’s why we had to use the “getRestParams()
” from Yii.
And we see that it looks like the data has been updated, Kermit is now a blue frog. If we go back to our index and check, yes, Kermit is now blue. So, we have index working, we have create working, we have view and we have update. So, the only thing left to do is to implement the delete.
Back to our code, delete is pretty simple. We’re just loading the model, delete it and we need to send a response. So, if we delete it here, there won’t be a model to respond. So, it depends how you want to implement delete. But usually, I will just send back the original data, so that you have it.
If I call delete here, the model will not actually exist in the database anymore. But I think its good practice to just send it back to the front end so that people know what has been deleted.
So, now, let’s try that. We are going to make a delete request and we don’t need to send any parameters. And we are going to delete Kermit, poor Kermit. So, we get our response.
If we check out our index, we just see, only Hypnotoad shows up. We can look at it here as well and just, yeah. Kermit is gone.
So, if we try to delete Kermit again, we see we get an error. It didn’t work correctly. So, this is not ideal, right? Because of instead of returning Json, we’re returning HTML.
So, let’s change that a bit. So, what we’re going to do is create an exception that implements the Ajax response interface. So, here, under “loadModel()
,” this is where it’s being called from. So, we tried to delete Kermit again. We are trying to load model with that ID. It’s null, so Yii is throwing a CHttpException.
Instead of doing that, we want to throw our own exception. So, let’s call it “Not found exception.” And let’s put braces around here. And if we go back to components and create that “Not found exception,” exceptions always need to extend from the exception class. We’ll say our message is “Resource not found. Please check that the ID exists.”
And we want it to implement the Ajax response interface. So, we need to get response data and we’ll just return empty. Because this is an error, so there’s really no data to return. We’ll get errors.
And we’ll just return the message. I put it in array because we’re returning errors and so, it’s expecting an array. Just to keep things consistent.
So, just reviewing what I did here is that Yii was throwing an HTTP exception, which was caught and is used to display HTML. We are changing it so that instead of HTML being displayed, this “Not found exception” will be thrown instead. So, now, we have to catch that.
So, going back to “sendAjaxResponse()
,” sorry… let’s see. We don’t want to throw the exception. We want to send it. So, if we threw the exception, it would be caught and then handled by Yii, which would be spitting out the HTML. So, we want to handle this ourselves.
So, first off, we have to see if we are actually getting data back or the exception. So, the first part is to check “Do we have errors?” “Success” means that there are no errors. And instead of sending an OK HTTP response code; if an error was thrown, we want to send a 404 code. So, we will see “Is it successful?” Yes, then it’s 200. No is 404.
And let’s include success as well. Usually, I like to do that just to make it easy on the front end. So, there we go. Let’s give that a try. So, we’re going to try to delete Kermit again and there we go. We see success is false and we have the error there being printed out.
So, there we go. We’ve created all the necessary actions index; delete, update, create and view for RESTful Ajax interface, API interface. And you’ll see how lengthy… the actions are pretty simple. This is just one line, a few lines, four to three lines. Usually, you know you’re writing good controller code when you can keep the controller action code very simple. Any controllers that are more than, say, ten lines should start sending off alarm bells that maybe some of the code should be moved into the model class instead.
Try to keep the fat models, thin controllers guidelines in mind when developing your controllers. This “sendAjaxResponse()"; probably, it’s better off in the parent controller class, because it’s not something just for the frog controller. But you’ll want to use it throughout other controllers in your app, so we need to make that protected instead of private.
So, at the beginning, I said about the URLs that instead of doing index.php?r=frog/delete/id/1
. Instead, we want to do more friendly URLs. So, under your config main.php file, under “components,” you see “Uncomment the following to enable URLs in path format.” So, if we uncomment that and then, go to curl localhost:8000/frog
, we see now we are getting it and we don’t have to do the index.php anymore. In fact, index.php will now not work. There we go.
So, that’s pretty much it. The code will be available as well if you need help following along. I hope you enjoyed this tutorial and you got a lot out of it. And happy Yii development.