[0:00] in this video you'll learn how to create a  complete restful api using plain object-oriented   [0:07] php and mysql all the code developed in this video  is free to download and use and the link is in the   [0:15] description to make api requests you'll need  an api client for example curl postman or http   [0:26] in this video i'll be using httpi on the command  line we'll start with the api endpoints first   [0:35] we'll create a new file called index.php in  here we'll start with the php opening tag   [0:42] and for now let's just print out the value of the  request uri element from the server super global [0:51] let's see what response we get from that page on  the command line using httpi we make a get request   [0:59] to that page and we see the response status code  which is 200 the response headers and the response   [1:06] body in the response body we see the output of  the vardump call and this is printing out the   [1:13] path part of the url index.php if we make that  request again but remove the index.php part as   [1:23] index.php is the default the request still works  and the value we get printed out is just the slash   [1:32] so this server variable contains the part  of the url after the host name as the api   [1:39] is going to be restful however we want a specific  endpoint url format a restful api consists of two   [1:47] urls one for a collection of resources and one for  an individual one that contains the resource id   [1:56] the result of calling the endpoint  depends on the combination of the url   [2:00] and the request method used so in order to have  restful urls we need to do some url rewriting   [2:09] as i'm using apache let's add a new file called  htaccess where we can specify server configuration   [2:16] settings in here we'll turn the rewrite engine on  then add a rewrite rule with a pattern of a dot   [2:24] that matches any url and we'll  substitute this with the index.php script   [2:30] basically this says that any url will now call  the index.php script no matter what it is these   [2:37] rules are only valid for apache and compatible  web servers and i'm deliberately keeping this   [2:43] very simple for the purposes of this video but if  you need the rewrite rules for another web server   [2:49] let me know in the comments let's give that a try  now if i make a request to any url the index.php   [2:57] script is executed and we get the path part of  the url printed out as an aside with httpi the   [3:06] default request method is get so if we're making  a get request we don't need to specify the method   [3:13] also the http protocol is the default so  we don't need to specify that either now   [3:20] we have access to this part of the url from our  code we can pass it to get the segments we need [3:29] so instead of printing this out we'll  use the explode function to split it up   [3:34] using the forward slash character we'll  assign this to a variable and print that out [3:42] if we call the slash product endpoint then  we get an array with two elements the element   [3:48] with an index of one containing the string  products if we add an id to that endpoint   [3:54] then this is placed in an additional array element  we're just creating an api for products so if the   [4:02] url is something else we'll respond with a 404  status code which you'll no doubt be familiar with   [4:10] so instead of printing this out if  the first segment isn't products   [4:15] we'll call the http response code function  setting the status code of 404 and we'll   [4:21] exit the script as for the id this will be  the second segment or if it's not specified   [4:29] we'll default it to null for now let's  just print that out to see what it contains   [4:36] if we make a request to an end point that isn't  slash products we get the 404 not found response   [4:44] if we make a request to the collection endpoint   [4:47] we get a response with a status code of 200 which  is the default success response code and the id   [4:54] is null if we add the id to  the url we get this printed out [5:01] note that for the purposes of this  video i'm keeping the routing simple   [5:06] with a simple rewrite rule and passing the  url manually like this in a more complex   [5:12] project you'd probably use a third-party router  component like fastroot to provide the routing   [5:19] next let's add a controller class to control the  response to this first let's add a new folder   [5:26] to store the class files called source then a  new file in here called productcontroller.php [5:34] in here we'll add the php opening tag and the  class definition next let's add a public method   [5:43] to this called process request the response  depends on the request method and if there's a   [5:49] resource id or not so let's add a string argument  for the method and a string argument for the id   [5:57] to keep it simple we'll just echo out the response  from this method so we'll declare the return type   [6:03] as void as we just saw when we made a request the  id can be null so we'll make this type declaration   [6:10] nullable inside this method for now let's just  print out the values of these two arguments   [6:19] back in the index script as we're using type  declarations let's enable strict types at   [6:25] the top of the script then in order to use the  class we just added we need to require the file   [6:32] that it's in instead of explicitly doing this  as we'll be adding more class files later on   [6:39] let's add an auto loader to load class files  automatically we'll do this by calling the spl   [6:45] auto load register function passing in  a callback function that will be called   [6:50] whenever a previously unused class is referenced  passing in the name of that class as an argument   [6:58] in the body of the function we'll require that  class in the source folder using the dir constant   [7:04] to reference the directory of the current file the  class variable passed into the function contains   [7:11] the name of the class you are trying to use so as  the file name matches the class name we can just   [7:18] add the dot php suffix and we'll get the right  file name in a larger project you'd probably use   [7:25] composer's autoloader but again for the purposes  of this video we're keeping it as simple as we can   [7:32] then at the end of the script now we can  use the controller class we just added   [7:37] so instead of printing out the id  variable we'll create a new object   [7:41] of the product controller class then we  can call the process request method on this   [7:47] passing in the request method which we can  get from the server super global then the id [7:55] so now if we make a request to slash  products the controller class is loaded   [8:00] automatically and we get the request method  printed out which is get and the id is null   [8:07] if i add the id then we get that printed out too   [8:12] if i change the request method for example  to post then this is what is printed out   [8:19] back in the controller now we can actually process  the request instead of printing out this message   [8:26] we're going to base the response on the request  method and the id if there's an id then it's a   [8:32] request that affects a single resource and if  not it's a request that affects a collection of   [8:38] resources instead of doing all the processing  in one method let's add separate methods for   [8:44] each one of these a process resource request  method passing in the request method and the   [8:50] id as arguments and a process collection request  method to which we'll just pass the request method   [8:59] then back in the process request  method we can call these two methods [9:07] let's start by processing the collection requests [9:12] a get request to slash products should return a  list of products and a post request will create a   [9:20] new product so in the process collection request  method let's check the request method with a   [9:26] switch statement if it's get will return a list of  products the standard format of an api response is   [9:34] javascript object notation or json so let's start  by just outputting an array containing a sample   [9:42] element encoded as json then a break statement  to end the case block if we make a get request   [9:50] to slash products then we see the array in the  response body however if we look at the content   [9:57] type response header it's set to text html  which is the default as we're returning json   [10:05] we need to set this to the json content  type instead of doing this in here as every   [10:13] response we send back from the api will be in  json format we'll do it in the index script   [10:20] so just after we set up the auto loader we'll call  the header function passing in a string to set the   [10:26] content type header to application json and also  additionally specifying the character set as utf-8 [10:36] so when we make the same request the  content type header is set to json   [10:41] plus using httpi the response body now  contains additional color formatting   [10:49] now we have this setup we can return some  actual data instead of this hard-coded value   [10:56] i'm going to create a database  quickly by running this sql   [11:01] this will create a database called productdb  and a table in that database called product   [11:08] this table contains various  columns of different types   [11:12] to demonstrate how these are handled in an api  client and how they're json encoded there's a   [11:18] link to the source code that includes this sql  in the description of this video let's run that [11:29] and there's the database and the product table [11:34] while we're here let's just insert a  couple of example records into this table   [11:39] we don't need to provide values for the id  column as being an auto increment column   [11:45] values for this will be assigned automatically  if we browse the table there are the two records [11:53] now we can connect to the database from  php first let's add a class to represent   [11:59] the database connection so we'll add a new  file in the source folder called database.php   [12:07] in here we'll add the php opening tag and the  class definition to connect to the database   [12:14] we need details such as the hostname database name  username and password let's pass these values in   [12:21] when we create an object of this class using  the constructor method we'll add arguments for   [12:27] the database host database name username and  password if you need any other values like the   [12:34] port for example then you can include them here  we'll use constructive property promotion to   [12:41] automatically create private properties for each  of these and assign the values of these arguments   [12:47] to these properties next let's add a method called  get connection which will return a pdo object   [12:55] in the body of this method let's create a variable  for the dsn containing the hostname and database   [13:01] name using the values of the properties we  created above then we'll create a new pdo   [13:08] object passing in the dsn username and password  and we'll return this object from the method [13:16] in the index page let's create an object  of this class passing in the database   [13:21] host name username and password i'm using the  root account which by default has a blank password   [13:28] this is fine when you're developing locally but  on a production machine the connection should   [13:34] have its own username and password also to keep  it simple i'm going to hard code these values in   [13:40] here but in reality what you'd probably do is  have these settings in a separate config file   [13:46] to test the database code we just created let's  call the get connection method on this object [13:54] if we make a get request to slash products then  we get the same response as earlier with the   [13:59] json test data this means that the class has  been loaded automatically and we connect it to   [14:05] the database successfully however let's see what  happens if i get one of the database connection   [14:12] details wrong for example an incorrect password  now if we repeat the request we get an exception   [14:21] this is as expected however by default  the exception details are formatted as   [14:26] html we always want to return json  from the api even if there is an error   [14:34] to fix this we could put a try catch block around  the get connection method call and output the   [14:40] error details as json in the catch block instead  however let's add a generic exception handler that   [14:48] will output any unhandled exception as json we can  do this with the set exception handler function   [14:56] this takes a callback function that will be  executed when an unhandled exception occurs   [15:02] the single argument to this function is an object  of the throwable class so let's add a new file in   [15:10] the source folder called errorhandler.php  and in here we'll add the php opening tag   [15:17] and the class definition then let's add a method  called handle exception with the single argument   [15:24] of type throwball that represents the exception  that's been thrown and doesn't return anything in   [15:31] the body of the method we'll output some json that  contains various data from the exception object we   [15:38] can get the error code the error message the file  it occurred in and the line number in that file   [15:46] in addition to outputting the exception details  in the response body let's also set the http   [15:52] status code to 500 indicating that there is an  error on the server to use this class as the   [16:00] generic exception handler in the index file we  call the set exception handler function passing   [16:07] in a string that identifies the handle exception  method in the error handler class we just added   [16:14] we do this as early as possible in this script  so that it's enabled as soon as possible   [16:19] but so that the class is loaded automatically we  have to do it after we've created the autoloader   [16:26] let's make the api request again and now  we get the error details encoded as json   [16:32] along with the 500 internal  server error http status   [16:38] now we've tested the error handler we can change  the password back to the correct one next we can   [16:45] use this database connection to make a query  to the database we'll do this in a separate   [16:51] class we'll start by adding a new file in  the source folder called productgateway.php [16:58] this class uses the table gateway pattern  basically its methods serve as a gateway to the   [17:04] product table in the database we'll add the php  opening tag and the class definition the methods   [17:11] in this class depend on a database connection  so we'll pass an object of the database class   [17:17] in using the constructor all the methods in this  class are going to use the database connection   [17:23] so instead of using constructor property  promotion to simply store this value in a property   [17:29] let's add a private property to the class to  store the pdo connection object then in the   [17:35] body of the constructor method we'll call the get  connection method on the database object and store   [17:40] this in the property then let's add a method  to get all the records from the product table   [17:47] we'll add a new public method called get all and  this will return an array in the body of this   [17:54] method we'll add a variable containing the sql  to select all the records from the product table   [18:00] we'll keep it simple with no order by clause or  limit or anything like that just the sql to select   [18:07] all of the records then we'll call the query  method on the connection object passing in the sql   [18:15] and assigning the returned pdo statement object  to a variable we want to return an array of rows   [18:23] so let's initialize a variable with an empty array  then we'll use a while loop to fetch each row in   [18:29] turn as an associative array inside the loop  we'll append the row to the array and finally   [18:36] in this method return the array of row data now we  can call this method to query the database which   [18:44] we'll do here in the product controller class  however instead of creating an object of the   [18:50] product gateway class in here we'll pass one in as  a dependency so let's add the constructor method   [18:57] with a product gateway object argument and  we'll add a private visibility modifier to this   [19:03] so that the value of this argument will  be assigned to a private property with the   [19:07] same name then in the process collection request  method instead of outputting this hard-coded array   [19:16] we'll call the get all method on the gateway  property to get this working back in the index   [19:22] script we just need to stitch all this together  so instead of calling the get connection method   [19:28] in here we'll create a new product gateway object  passing in the database as an argument then we   [19:35] can pass this gateway object in as an argument  when we create the product controller object   [19:43] let's give that a try now if we make the same  request again we get the json containing the   [19:48] two records from the database that we inserted  earlier note however that the values for each   [19:54] record are encoded as strings even though in the  database the id and size columns are integers   [20:01] and the is available column is boolean this is  because pdo is converting these values to strings [20:10] if the pdo stringify fetch's attribute is true   [20:13] then all values will be converted to  strings so we need to set this to false   [20:19] we also need to set the emulate prepares  attribute to false so that this works [20:26] so in the database class when we create a  new pdo object we'll add a fourth argument   [20:32] that's an array and we'll set both the emulate  prepares and stringify fetch's attributes to false   [20:40] now if we make the api request again the  numbers have no longer been converted to strings   [20:46] note however that the boolean value is also  a number one or zero this is to be expected   [20:52] as internally the database server stores boolean  values as one for true and zero for false if   [21:00] you want these to be boolean literals in the  json then they have to be converted manually [21:07] so in the product gateway class inside the  while loop where we're getting each row from the   [21:12] database before we append the row to the array  we'll cast the is available column to boolean [21:20] now when we make the api request the is  available column is output as a boolean literal   [21:26] true or false so when we make a get request  to the collection url slash products we get   [21:33] a list of all the products next let's add the  functionality to create a new product which we   [21:39] do by making a post request to the same url so  in the product controller class in the process   [21:48] collection request method we'll add a new case to  the switch statement for when the request method   [21:54] is post first we need to get the data from the  request with a post request for a regular web form   [22:02] that you would submit from a browser the data from  that form would be available in the post array   [22:08] so let's assign that array to a variable and  print it out to see what we get if we make a post   [22:15] request to the collection endpoint with some data  in the request we get an empty array printed out   [22:22] by default httpi sends data in the request encoded  as json as the api is responding with json encoded   [22:31] data we'll require that data sent to the api is  encoded as json also instead of the post array   [22:40] we get json data from the request  directly from the php input stream   [22:46] we do this by using the file get contents function  passing in this string to identify that stream [22:55] now if we make the same request we get a string  containing some json let's decode that json   [23:03] using the json decode function passing in true as  the second argument to get an associative array   [23:11] if we make the request again now we have an  associative array containing the data from the   [23:16] request if we add another item of data this adds  a new element to that array note that with httpi   [23:25] by default values are encoded as strings to send  a literal integer or boolean value for example   [23:33] we need to add a colon before the equal sign now  the value for that field is encoded as an integer   [23:40] note what happens though if we make a post  request but don't include any data in the request   [23:47] we get null to make the api more robust  and handle this possibility we need to   [23:54] check that this value isn't null the json decode  function will return null if the json is invalid   [24:01] or if there isn't any data we could check for  this with an if statement but a simpler way to   [24:07] do it is to just cast this value to an array  if the value is already an array then nothing   [24:14] happens if it's null then it will be converted to  an empty array if we make an empty post request   [24:22] again now we get an empty array instead of null  now we have an array of data from the request   [24:30] we can insert a new record into the database  to do this first in the product gateway class   [24:37] let's add a new public method called create with  an array argument for the data inside this method   [24:44] we'll add a string containing the sql to insert  a new record into the product table specifying   [24:50] the name size and is available columns with  placeholders for the values then we'll use   [24:58] a prepared statement for this so we'll call  the prepare method on the connection property   [25:03] passing in the sql then we can bind the values to  the placeholders first the name which is a string [25:14] then the size which will default to zero if it  wasn't supplied binding its value as an integer   [25:21] finally the is available column which will  default to false and we'll cast this to boolean   [25:27] just in case the value was supplied as another  type and we'll bind this as a boolean value   [25:34] then we'll execute the statement and we'll  return the id of the record that was inserted   [25:40] which we can get by calling the last  insert id method this method returns   [25:46] a string so we'll add the string return  type declaration to the function definition [25:54] then back in the controller instead of  outputting the data from the request   [25:59] we can call the create method on the gateway  property passing in the array of data this   [26:06] returns the id of the newly created record so  we'll assign that to a variable for the response   [26:13] to this request we'll output some json with  a message saying that the product was created   [26:18] and its id finally we'll add a break  statement to end the case block [26:26] let's give that a try if we make a post request to  the product's endpoint passing in just a product   [26:32] name we get a reply saying the product with an  id of 3 was created if we include the size we get   [26:41] a product with an id of 4 and if we include  the is available field we get product id 5. [26:50] in the database we can see the three new  records that we just inserted using the api   [26:56] where we didn't specify values for the size and  is available columns the values for these were   [27:02] defaulted note that in the output when we  make this request we have a status code of   [27:09] 200. this is the default status code if we don't  change it however when inserting a new record the   [27:17] status code to use is 2 0 201 so let's set  that using the htt response code function [27:27] now if we make a successful request to create a  new product we get the 201 created status code   [27:34] watch what happens though if we make a request  with no data at all we get a couple of errors   [27:41] the first tells us we have an undefined array  element the second is a database error that   [27:47] comes from trying to insert a record with  no value for the name column however note   [27:53] that the first error isn't encoded as json even  though we configured a generic exception handler   [28:00] what's actually happening here is that a  php error has occurred which isn't caught   [28:06] by the custom exception handler to catch the  error we have to set a custom error handler   [28:12] using the set error handler function the callback  function we passed to this has this signature with   [28:18] the error number message and so on so first in the  error handler class let's add a new public method   [28:26] called handle error which fits this signature  although we only need the first four arguments [28:36] inside the method instead of repeating  what we have in the exception handler   [28:40] we can just throw a new exception  of the error exception class   [28:45] this exception class was designed to  represent errors as exceptions the   [28:50] constructor takes various different arguments  for the various attributes of the exception   [28:57] so in the body of the handle error method we'll  throw a new error exception passing in the error   [29:03] message zero for the exception code as we don't  have this the error level file where it occurred   [29:10] and the line number in that file then in the  index script before we set the exception handler   [29:17] we'll call the set error handler function  passing in the callback function we just added [29:25] now if we make a post request without  any data we get the error encoded as json   [29:31] we also no longer get the other exception as  execution halted after this exception occurred   [29:37] which is as expected now we can look at  the reason why this error is occurring   [29:43] we're getting an undefined array key error  on line 38 of the product gateway class [29:51] on that line we're trying to use  an array element that doesn't exist   [29:55] this is because we didn't send any data with  the request so before we try and save the   [30:01] data to the database we need to validate it  we'll do this in the product controller class   [30:08] let's add a new private method called get  validation errors this will have one argument   [30:14] which is the array of data to validate  and will return an array of error messages   [30:20] in the body of the method first we'll initialize  an empty array then we'll validate the name field   [30:28] we'll keep this simple and just  check to see if it's empty or not   [30:33] if it is we'll append an error  message to the errors array [30:39] next we'll validate the size this is optional so  first we'll check if a value has been supplied   [30:47] if so we'll use the filter var function with  the integer validation filter to check that   [30:52] it's an integer if it's not this function will  return false so we'll explicitly check for that   [31:01] the reason we check it's specifically boolean  false and not just negate the result of the   [31:06] function call is that it could be zero which  is still a valid value but this evaluates to   [31:12] boolean false if it's not valid we'll append  a suitable message to the errors array finally   [31:20] at the end of the method we'll return the array  then back in the process collection request method   [31:27] before we call the create method we'll call the  method we just added passing in the data from   [31:32] the request and assigning the return value  to a variable if this variable isn't empty   [31:40] then we'll output the errors as json in  the response body and break out of this   [31:44] case block we'll also return an http status  code of 422 which is unprocessable entity [31:55] let's give that a try now if we make a  request with no data we get the error   [32:00] in the body of the response and a 422 status code   [32:05] if we add an invalid size to the data  then we get the error message for that too   [32:12] if we send valid data then the record is  created and we get the 201 status code   [32:19] so now we can make a get request to this  collection endpoint to get a list of all products   [32:25] and a post request to create a new one there  are other http request methods we can use for   [32:32] example delete if we make a delete request to this  endpoint we get an empty response body with a 200   [32:40] status code we should respond differently if this  method isn't allowed for this endpoint so let's   [32:49] add a default block to the switch statement and  in here we'll respond with a 405 status code which   [32:56] is method not allowed when responding with this  status code the standard requires you to include   [33:03] the methods that are allowed in an allow header  so we'll add the call to the header function   [33:09] with an allow header specifying that get and post  are the only methods allowed on this endpoint [33:17] now if we try an invalid method we get the  method not allowed response and the allow   [33:23] header is included if we try a valid method such  as get we get a successful response as expected   [33:33] that completes the processing for the collection  request next let's process requests where the   [33:39] product id is included in the endpoint the  first thing we need to do for these requests   [33:46] is to check that a record already exists in  the database for the given id in the product   [33:53] gateway class let's add a new public method  called get with a string argument for the id   [34:01] as above we'll use a prepared statement for this  so first we'll write the sql to select the record   [34:07] from the product table using a placeholder  for the id then we'll prepare the statement   [34:13] passing in the sql and bind the value  of the id argument as an integer [34:21] next we'll execute it and fetch the record as  an associative array finally in this method   [34:28] we'll return the data the fetch method will  return an array if the record was found   [34:34] are false otherwise so we'll add the appropriate  return type declaration to the function definition [34:43] then in the controller in the process resource  request method we can call the get method on the   [34:49] gateway property passing in the id then for now  let's just output the return value encoded as json [35:00] let's give that a try if we make a get request  to the slash products slash one endpoint   [35:07] then we get the product record in  the response body in json format   [35:12] note however that the is available column  is an integer instead of a boolean value [35:19] in the get all method in the product gateway  class we manually converted this column to a   [35:25] boolean value when we retrieve the data  let's do the same in the get method so   [35:32] once we fetch the data if it isn't  false we'll cast its value to boolean [35:42] now if we make the same request again  the is available column is boolean   [35:47] this request worked because  a record with an id of 1   [35:51] exists in the database if we make a request  with an id that doesn't exist we just get false [36:00] back in the product controller  class let's check for this   [36:04] and if the product wasn't found  we'll respond with a 404 status code   [36:09] a suitable message in the response body and we'll  exit from the method with a return statement [36:16] now if we make the same request  we get the 404 not found response [36:23] next we can process the request  based on the http request method used   [36:28] as before we'll do this with a switch  statement on the method argument   [36:34] if the method is get we'll do what we're already  doing which is to output the product data as json   [36:41] then we'll break from the case block we  don't need to specify the status code as 200   [36:46] as this is the default but you can if you want to  so if we make a get request to the endpoint for   [36:54] a product that exists we get a 200 response  with the product data in the response body [37:02] next we'll process a request  to update an existing record   [37:07] we'll do this with a patch request so let's add  a patch case to the switch statement in here   [37:15] most of this will be similar to what we're doing  down here when we insert a new record so let's   [37:21] copy this whole case block and paste it in the  patch block the first part will be the same where   [37:28] we get the data from the request and validate  it however instead of calling the create method   [37:35] on the gateway object we need a different method  to update an existing record so let's add a new   [37:42] public method to the product gateway class called  update we'll pass in the current product details   [37:50] and the new details from the request data and  we'll return an integer which will be the number   [37:56] of rows in the database that were updated in here  we'll add the sql to update a record identifying   [38:03] the record with a where clause for the id we'll  prepare the statement as before passing in the sql [38:13] then we can bind values to the placeholders  starting with the name if a new value has been   [38:19] passed in the request we'll use that otherwise  we'll just use the existing value if the value   [38:25] hasn't changed the database will detect this and  not update anything we'll do the same for the size   [38:33] and is available columns then we'll bind the id  value which we get from the current product values   [38:42] and we'll execute the statement we'll  call the row count method to set the   [38:47] return value from this method then back in the  controller instead of calling the create method   [38:55] we call the update method we just added passing  in the current product as the first argument   [39:02] this returns the number of rows affected so we'll  change the name of the variable we assign this to   [39:08] accordingly for the response we'll use the  200 status code so we can remove this call   [39:14] to the http response code function as this  is the default for the body of the response   [39:21] we'll include the id in the message and  return the number of rows that were affected [39:28] let's give that a try we'll send a patch request  to the product with an id of one updating the name   [39:36] we get a successful response telling us one row  was affected if we make a get request to the same   [39:42] endpoint then we see the name value has indeed  been updated if we make the same patch request   [39:49] again then no rows were affected as no values  were changed we can update all of the columns [39:59] and the validation works as before however if  we just want to update the size for example   [40:07] then we get a validation error saying that  the name is required the name is only required   [40:13] when we're creating a new record so we need  to do some conditional validation on this   [40:20] in the get validation errors method we'll only  add this validation message for the name field   [40:26] if we're creating a new record so let's add a new  boolean argument to the method to identify if it's   [40:33] a new record or not which will default to true  then before we check if the name value is empty   [40:41] we'll check if this variable is true then  where we're processing the patch request   [40:48] we can pass in false as the second argument  when we call the get validation errors method   [40:55] let's give that a try if we make  the same patch request as before   [40:59] just passing in a value for the size now we  get a response saying the record has been   [41:04] updated and if we view that record we can  indeed see that the value has been changed [41:12] the final operation we want to carry out on  a resource is to delete it so let's start   [41:18] in the process resource request method by  adding a delete case to the switch statement   [41:25] then in the product gateway class we'll add a  new public method called delete passing in the   [41:31] id as a string and returning an integer in the  body of the method we'll add the sql to delete   [41:38] a record from the product table identified  by the id we'll prepare the statement bind   [41:45] the id value to the placeholder and execute it  finally we'll return the number of rows affected   [41:55] back in the controller we'll call this method  on the gateway property passing in the id and   [42:01] assigning the return value to a variable for  the body of the response we'll output a message   [42:07] the number of rows that were affected and  we'll end the case block with a break statement [42:14] let's give that a try if we send a  delete request to the product with   [42:18] an id of one then we get a result saying the  product was deleted and one row was affected   [42:26] if we then try to read that resource by sending  a get request to the same endpoint then we   [42:31] get a 404 response saying the product wasn't  found confirming that the record was deleted   [42:38] one final thing we need to do in here is  respond to any request that doesn't use one   [42:43] of the above methods as we did below so let's add  a default block set the http status code to 405   [42:52] and send an allow header with the  allowed methods of get patch and delete [42:59] so now if we send a post request for example  to the endpoint for the product with an id   [43:05] of 2 we get the 405 method not allowed response  including the allow header so now you know how   [43:14] to create a restful api using plain object  oriented php and mysql we can make requests   [43:23] to list all resources to create one to show  an individual resource update it and delete it [43:35] there's a link to all the code shown  in this video in the description   [43:40] along with links to sites shown  and relevant documentation   [43:45] please don't forget to like comment and  subscribe and as always thank you for watching