Buy

Project Routing

Ok, let’s get start by downloading (see the Download button for subscribers) or cloning the CodeBattles project. Now, follow the README.md file to get things working. It involves downloading Composer and installing the vendor libraries:

$ git clone https://github.com/knpuniversity/rest.git
$ cd rest
$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar install

Tip

If you’re new to Composer, watch The Wonderful World of Composer.

When that’s done, start up the app by using PHP’s awesome built-in web server:

$ cd web
$ php -S localhost:8000

Note

The built-in web server requires PHP 5.4, which all of you should have! If you’re using PHP 5.3, you’ll need to configure a VirtualHost of your web server to point at the web/ directory.

If it worked, then load up the site by going to http://localhost:8000. Awesome!

About the App

CodeBattles is built in Silex, a PHP microframework. If this is your first time using Silex, take a few minutes with its Documentation to get to know it. It basically let’s us design routes, or pages and easily write the code to render those pages. Our setup will look just a little bit different than this, but the idea is the same.

But this is not a tutorial on building a REST API on only Silex! Most of what we’ll do is basically the same across any framework. You will need to do a little bit of work here and there. But trust me, these things are a pleasure to do compared with all the tough REST stuff.

First Endpoint: POST /api/programmers

Let’s pretend we’re building the API for an iPhone app. Ignoring authentication, what’s the first thing the user will do in the app? Create a programmer of course! And that’s our first API endpoint.

Separate URLs from our Web Interface?

But hold up! In the web version of our app, we’re already able to create a programmer by filling out a form and submitting it via POST to /programmers/new. This either re-renders the HTML page with errors or redirects us.

Why not just reuse the code from this URL and make it work for our API? To do this we’d need to make it accept JSON request data, become smart enough to return errors as JSON and do something other than a redirect on success. Then, /programmers could be used by a browser to get HTML or by an API client to pass JSON back and forth.

That would be sweet! And later, we’ll talk about how you could do that. But for now, things will be a lot easier to understand if we leave the web interface alone, prefix our API URLs with /api, and write separate code for it.

This does break a rule of REST, because each resource will now have 2 URLs: one for HTML and one for the JSON representation. But REST has a lot of rules, too many for our rebel Codebattles site. We’ll break this one, like many APIs. But later, I’ll show you how we could use 1 URL to return multiple representation.

Basic Routing

So let’s build the endpoint. Find the src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php file and uncomment the route definition:

// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...

protected function addRoutes(ControllerCollection $controllers)
{
    $controllers->post('/api/programmers', array($this, 'newAction'));
}

Next, create a newAction inside of this class and return let's battle!:

// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...

public function newAction()
{
    return 'let\'s battle!';
}

And just like that, we’re making threats and we have a new endpoint with the URL /api/programmers. If we make a POST request here, the newAction function will be executed and these famous last words will be returned in the response. This is the core of what Silex gives us.

URLs and Resources

My choice of a POST request to create a programmer isn’t accidental. /api/programmers is a collection resource. And according to some HTTP rules I’ll show you later, when you want to create a resource, you should send a POST request to its collection.

In other words, I’m not making this all up: I’m following the rules of the web. And in the API world, if you follow the rules, you’ll have more friends.

Testing the Endpoint

Well let’s try it already! That’s actually not easy in a browser, since we need to make a POST request. Instead, open up the testing.php file at the root of the project that I’ve already prep’ed for us:

// testing.php
require __DIR__.'/vendor/autoload.php';

use Guzzle\Http\Client;

// create our http client (Guzzle)
$client = new Client('http://localhost:8000', array(
    'request.options' => array(
        'exceptions' => false,
    )
));

This is a plain PHP file that creates a Guzzle Client object. Guzzle is a simple library for making HTTP requests and receiving responses.

Let’s make a POST request to /api/programmers and print out the response:

// testing.php
// ...
$client = new Client('http://localhost:8000', array(
    'request.options' => array(
        'exceptions' => false,
    )
));

$request = $client->post('/api/programmers');
$response = $request->send();

echo $response;
echo "\n\n";

Try it out by running the file from the command line. You’ll need to open a new terminal tab and make sure you’re at the root of the project where the file is:

$ php testing.php
HTTP/1.1 200 OK
Host: localhost:8000
Connection: close
Cache-Control: no-cache
Content-Type: text/html; charset=UTF-8

let's battle!

Success!

Leave a comment!

  • 2016-08-18 weaverryan

    Ah, thanks for the clarification - I looked too quickly and didn't notice which tutorial you were asking about! I'm glad you got it working - despite my not-so-perfect help :).

    And yep, we're here if you have any comments or questions - both help everyone.

    Cheers!

  • 2016-08-18 3amprogrammer

    Thanks for the replay. I have no idea what the heck was wrong, but when I removed the files and pasted it again everything magically worked - probably it was some issue with permissions, I have no idea.

    And by the way, I think you misunderstood me. Homestead works well with Symfony all it needs is to add type: Symfony to Homestead.yaml.
    I had a problem of running regular app from REST course using index.php as a root, not app.php or app_dev.php.

    Nevertheless, everything is working fine now. I really appreciate I can count on you when any questions pop out :)

  • 2016-08-15 weaverryan

    Hey 3amprogrammer!

    Yea, Homestead is awesome! We have some docs about specifically using it with Symfony, in case you haven't seen it already :) http://symfony.com/doc/current...

    But specifically, the issue is that Symfony doesn't have an "index.php" file - it has "app.php" and "app_dev.php". The best way to solve this is either to:

    A) Look back at your homestead setup - it has configuration for symfony, which should take care of this for you
    B) Look at the Nginx documentation for Symfony - http://symfony.com/doc/current... - and tweak your setup to match this.

    In both cases, what you basically need to change is to tell Nginx to execute either app.php or app_dev.php, instead of index.php.

    Let me know if this helps! Cheers!

  • 2016-08-13 3amprogrammer

    I am using laravel/homestead box and when I try to access code-battles.dev my browser downloads the file, but when I type code-battles.dev/index.php everything works just fine. I have tried to do some googling, but yet without any success.

    This is my conf file. Can you please give me some hint as I dont know nginx much.


    server {
    listen 80;
    listen 443 ssl http2;
    server_name code-battles.dev;
    root "/home/vagrant/Code/code-battles/web";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
    try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt { access_log off; log_not_found off; }

    access_log off;
    error_log /var/log/nginx/code-battles.dev-error.log error;

    sendfile off;

    client_max_body_size 100m;

    location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_intercept_errors off;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 4 16k;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    }

    location ~ /\.ht {
    deny all;
    }

    ssl_certificate /etc/nginx/ssl/code-battles.dev.crt;
    ssl_certificate_key /etc/nginx/ssl/code-battles.dev.key;
    }
  • 2016-08-04 weaverryan

    Glad you got it figured out Ken! I'm not sure what went wrong - I just downloaded the code and it worked ok (I double-checked that it *did* install the Symfony Finder library that seemed to be missing for you). Perhaps there was a momentary download issue the first time you ran composer install.

    Cheers and good luck!

  • 2016-08-04 Victor Bocharsky

    Hey Ken,

    Most likely, you have missed composer dependencies, so just try to run "composer install" first and it should help. And sure, you can configure a virtual host for your Apache real web server, check the official Apache configuration example. Ensure you use configuration example for your version of Apache.

    Cheers!

  • 2016-08-03 Ken

    The problem seemed to be in the compsor.json file. I removed the require-dev statement and it seems to work on my Lamp stack

  • 2016-08-03 Ken

    I can't get started. I run the webservere in the terminal window,

    When I the localhost:8000 I get the following error
    [Wed Aug 3 11:43:42 2016] PHP Fatal error: Class 'Symfony\Component\Filesystem\Filesystem' not found in /home/ken/NetBeansProjects/rest/src/KnpU/CodeBattle/DataFixtures/FixturesManager.php on line 29
    [Wed Aug 3 11:43:42 2016] PHP Stack trace:
    [Wed Aug 3 11:43:42 2016] PHP 1. {main}() /home/ken/NetBeansProjects/rest/web/index.php:0
    [Wed Aug 3 11:43:42 2016] PHP 2. require_once() /home/ken/NetBeansProjects/rest/web/index.php:6
    [Wed Aug 3 11:43:42 2016] PHP 3. KnpU\CodeBattle\DataFixtures\FixturesManager->resetDatabase() /home/ken/NetBeansProjects/rest/app/bootstrap.php:31
    [Wed Aug 3 11:43:42 2016] 127.0.0.1:40294 [200]: / - Class 'Symfony\Component\Filesystem\Filesystem' not found in /home/ken/NetBeansProjects/rest/src/KnpU/CodeBattle/DataFixtures/FixturesManager.php on line 29

    Is there a way to either run the code on my nomal apache feed or do you have a fix for this

    Ken

  • 2016-05-31 weaverryan

    Wow, that *is* weird! But, I like your debugging techniques :). At this point, we haven't added any security, so it seems like the 403 is probably not coming from our Silex app (but, I could be missing something). Does the 403 (if you print out the response content in the test file) "look" like it's coming from your web server? Are you using the built-in web server? What happens if you try to curl the URL directly - e.g. curl http://localhost:8000/...

    I'm sure we can figure this out :)

  • 2016-05-26 ciudadano82

    Hi guys! i'm having a problem with the testing file. It throws me a 403 error. However, when i run it from the browser, it throws me the right response (i've changed the post() call to a get() call to prove it there, and also changed the guzzle request to a get call). Any help?

  • 2016-05-22 weaverryan

    Hi Florin!

    Ah, I know this problem :). This is a classic problem when you first install php - you need to set the date.timezone setting in php.ini. First, you need to find your php.ini file. If you're using the built-in php web server, you can find it by running this from the command line:

    php --ini

    Look for "Loaded Configuration File". That's your php.ini path. Open that file in an editor (you'll probably need sudo), and find the date.timezone line. Make sure it is not commented out (remove the ; at the beginning of the line) and set it to your timezone (from this list http://php.net/manual/en/timez.... Mine looks like this:

    date.timezone = America/Detroit

    Then, kill your build-in web server (ctrl+c) and run it again. All should be well :)

    Cheers!

  • 2016-05-21 Florin

    I have installed php 5.4.45 and is working now, but i still have one warning:

    Warning: date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in /Users/user/rest/vendor/monolog/monolog/src/Monolog/Logger.php on line 214

  • 2016-05-21 Florin

    Hello, i am getting this error: RuntimeException in NativeSessionStorage.php line 144:
    Failed to start the session because headers have already been sent by "/Users/user/rest/vendor/monolog/monolog/src/Monolog/Logger.php" at line 214.
    PHP version is 5.527. Thank you!

  • 2016-03-14 weaverryan

    Hey there!

    Hmm, it just looks like your local web server isn't running. When you get this error, are you able to go to `http://localhost:8000/api/programmers` in your browser? I expect that you will also see a connection refused error when you do this. If so, check that you started your built-in web server correctly - the commands are near the top of the script on this page :).

    Cheers!

  • 2016-03-12 agape801

    Hello I am getting this error Fatal error: Uncaught exception 'Guzzle\Http\Exception\CurlException' with message '[curl] 7: Failed to connect to localhost port 8000: Connection refused [url] http://localhost:8000/api/programmers' in C:\Users\Agape\rest\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:359 ... I've tried 127.0.0.1 as well and it didnt work

  • 2015-07-14 weaverryan

    Hey!

    The project uses SQLITE, so this sounds to me like pdo-sqlite driver isn't installed/enabled in your PHP install. If you run "php -i | grep sqlite", you should see some output related to pdo_sqlite (or if you're on windows, you can just run "php -i" and look in the output for pdo_sqlite stuff). Anyways, I think you *won't* find anything there. In that case, check how you installed PHP to see how to get this enabled.

    Cheers!

  • 2015-07-14 Emberous

    Hello, i am getting this error: Fatal error: Uncaught exception 'PDOException' with message 'could not find driver' in /usr/share/nginx/html/test/rest/rest/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:40 Stack trace: #0