Buy

POST: Creation, Location Header and 201

POST: Creation, Location Header and 201

Once the POST endpoint works, the client will send programmer details to the server. In REST-speak, it will send a representation of a programmer, which can be done in a bunch of different ways. It’s invisible to us, but HTML forms do this by sending data in a format called application/x-www-form-urlencoded:

POST /api/programmers HTTP/1.1
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded

nickname=Geek+Dev1&avatarNumber=5

PHP reads this and puts it into the $_POST array. That’s ok for the web, but in the API world, it’s ugly. Why not, have the client send us the representation in a beautiful bouquet of curly braces known as JSON:

POST /api/programmers HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "nickname": "Geek Dev1",
    "avatarNumber": 5
}

Creating a request like this with Guzzle is easy:

// testing.php
// ...

$nickname = 'ObjectOrienter'.rand(0, 999);
$data = array(
    'nickname' => $nickname,
    'avatarNumber' => 5,
    'tagLine' => 'a test dev!'
);

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

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

The second null argument is the request headers to send. We’re not worried about headers yet, so we can just leave it blank.

Coding up the Endpoint

Back in the ProgrammerController class, let’s make this work by doing our favorite thing - coding! First, how do we get the JSON string passed in the request? In Silex, you do this by getting the Request object and calling getContent() on it. Let’s just return the data from the endpoint so we can see it:

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

public function newAction(Request $request)
{
    $data = $request->getContent();
    return $data;
}

Tip

Your framework will likely have a shortcut for getting the request content or body. But if it doesn’t, you can get it by using this strange bit of code:

$data = file_get_contents('php://input');

This is a special stream that reads the request body. For more details, see php.net: php://.

Try running our testing.php file again:

$ php testing.php

This time, we see the JSON string being echo’ed right back at us:

HTTP/1.1 200 OK
...
Content-Type: text/html; charset=UTF-8

{"nickname":"ObjectOrienter31","avatarNumber":5}

Creating the Programmer Resource Object

Awesome! I’ve already created a Programmer class, which has just a few properties on it. I also created simple classes for the other two resources - Project and Battle. We’ll use these later.

In newAction, we have the JSON string, so let’s decode it and use the data to create a new Programmer object that’s ready for battle. We’ll use each key that the client sends to populate a property on the object:

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

public function newAction(Request $request)
{
    $data = json_decode($request->getContent(), true);

    $programmer = new Programmer($data['nickname'], $data['avatarNumber']);
    $programmer->tagLine = $data['tagLine'];
    // ...
}

Note

Do not forget to add use KnpU\CodeBattle\Model\Programmer; namespace for the Programmer class at the beginning of the ProgrammerController.php.

My app also has a really simple ORM that lets us save these objects to the database. How you save things to your database will be different. The key point is that we have a Programmer class that models how we want our API to look, and that we can somehow save this:

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

public function newAction(Request $request)
{
    $data = json_decode($request->getContent(), true);

    $programmer = new Programmer($data['nickname'], $data['avatarNumber']);
    $programmer->tagLine = $data['tagLine'];
    $programmer->userId = $this->findUserByUsername('weaverryan')->id;

    $this->save($programmer);

    return 'It worked. Believe me - I\'m an API';
}

At the bottom, I’m just returning a really reassuring message that everything went ok.

Faking the Authenticated User

I’ve also added one really ugly detail:

$programmer->userId = $this->findUserByUsername('weaverryan')->id;

Every programmer is created and owned by one user. On the web, finding out who is creating the programmer is as easy as finding out which user is currently logged in.

But our API has no idea who we are - we’re just a client making requests without any identification.

We’ll fix this later. Right now, I’ll just make every programmer owned by me. Make sure to use my username: it’s setup as test data that’s always in our database. This test data is also known as fixtures.

Ok, the moment of truth! Run the testing script again:

$ php testing.php
HTTP/1.1 200 OK
Host: localhost:8000
...
Content-Type: text/html; charset=UTF-8

It worked. Believe me - I'm an API

The message tells us that it probably worked. And if you login as weaverryan with password foo on the web, you’ll see this fierce programmer-warrior in the list.

Status Code 201

But no time to celebrate! Our response is a little sad. First, since we’ve just created a resource, the HTTP elders say that we should return a 201 status code. In Silex, we just need to return a new Response object and set the status code as the second argument:

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

public function newAction(Request $request)
{
    // ...
    $this->save($programmer);

    return new Response('It worked. Believe me - I\'m an API', 201);
}

Running the testing script this time shows us a 201 status code.

Location Header

And when we use the 201 status code, there’s another rule: include a Location header that points to the new resource. Hmm, we don’t have an endpoint to get a single programmer representation yet. To avoid angering the RESTful elders, let’s add a location header, and just fake the URL for now:

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

public function newAction(Request $request)
{
    // ...
    $this->save($programmer);

    $response = new Response('It worked. Believe me - I\'m an API', 201);
    $response->headers->set('Location', '/some/programmer/url');

    return $response;
}

If you stop and think about it, this is how the web works. When we submit a form to create a programmer, the server returns a redirect that takes us to the page to view it. In an API, the status code is 201 instead of 301 or 302, but the server is trying to help show us the way in both cases.

Try the final product out in our test script:

$ php testing.php
HTTP/1.1 201 Created
...
Location: /some/programmer/url
Content-Type: text/html; charset=UTF-8

It worked. Believe me - I'm an API

Other than the random text we’re still returning, this endpoint is looking great. Now to GET a programmer!

Leave a comment!

  • 2018-06-07 Victor Bocharsky

    Hey Shaun,

    Thanks for sharing it with others. Yes, you need to use "base_uri" now, but why do you set exceptions to false? And why do you echo all that info? Is it just temporary for debugging purposes?

    Cheers!

  • 2018-06-06 Shaun

    For anyone using Guzzle version 6, this is what I used to get this working:


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

    $client = new \GuzzleHttp\Client([
    'base_uri' => 'http://localhost:8000',
    'defaults' => [
    'exceptions' => false,
    ]
    ]);

    $response = $client->post('/api/posttests');

    echo $response->getStatusCode() . ' ' . $response->getReasonPhrase();
    echo "\n";
    foreach ($response->getHeaders() as $name => $values) {
    echo $name . ': ' . implode(', ', $values) . "\r\n";
    }
    echo $response->getBody();
    echo "\n\n";
  • 2018-06-04 Victor Bocharsky

    Hey Shaun,

    Yes, Symfony recommends this way. But if you like Guzzle a lot - you can use Guzzle of course ;)

    Cheers!

  • 2018-06-03 Shaun

    In the symfony documentation they don’t use Guzzle for functional tests and they extended webtestcase. Is this the best practice now?

    https://symfony.com/doc/cur...

  • 2018-01-18 Victor Bocharsky

    Hey Pawel,

    Thanks for sharing your solution with others! Yeah, looks like you needed to upgrade "guzzlehttp/ringphp" to v1.1.x at least since you have only 1.0.7. Nice error message from Composer btw!

    Cheers!

  • 2018-01-18 Paweł Chry

    I had same issue as Shaun
    and command:

    composer require "guzzlehttp/guzzle:^5.3"


    throws:

      Problem 1
    - Installation request for guzzlehttp/ringphp (locked at 1.0.7) -> satisfiab le by guzzlehttp/ringphp[1.0.7].
    - guzzlehttp/guzzle 5.3.0 requires guzzlehttp/ringphp ^1.1 -> satisfiable by guzzlehttp/ringphp[1.1.0].
    - guzzlehttp/guzzle 5.3.1 requires guzzlehttp/ringphp ^1.1 -> satisfiable by guzzlehttp/ringphp[1.1.0].
    - guzzlehttp/guzzle 5.3.2 requires guzzlehttp/ringphp ^1.1 -> satisfiable by guzzlehttp/ringphp[1.1.0].
    - Conclusion: don't install guzzlehttp/ringphp 1.1.0
    - Installation request for guzzlehttp/guzzle ^5.3 -> satisfiable by guzzleht tp/guzzle[5.3.0, 5.3.1, 5.3.2].

    so I did:

    composer require "guzzlehttp/ringphp:^1.1"


    and then:

    composer require "guzzlehttp/guzzle:^5.3"

    and it works.

  • 2018-01-12 Vlad Catalin

    This worked for me!
    Thanks for your answer!

  • 2018-01-11 Victor Bocharsky

    Hey Shaun,

    Hm, first of all I wonder what PHP version do you use? Execute "php -v" to see.

    OK, that's a bit weird, but I'm starting to get lost here. Let me to summarize all we have and formulate a few steps for you:

    1. Let's obviously require "guzzlehttp/guzzle" if it can't be found by Composer to update. Since you're on Guzzle 5.2.0, let's avoid BC breaks and require "^5.3":
    composer require "guzzlehttp/guzzle:^5.3"

    2. Composer should install the latest 5.3.1 version of Guzzle lib. To double check, run:
    composer info guzzlehttp/guzzle

    IN the output you should see "versions : * 5.3.1" which means you're on the latest available version of Guzzle in 5.x branch for now.

    Then when you run "php testing.php" that "Fatal error: Cannot use lexical variable $eventName..." should be gone. If no, let us know, then you probably should upgrade to the latest version of Guzzle in 6.x branch or use a different PHP version.

    Cheers!

  • 2018-01-08 Shaun

    Same again unfortunately :(


    composer update guzzlehttp/guzzle
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Nothing to install or update
    Generating autoload files
    > Incenteev\ParameterHandler\ScriptHandler::buildParameters
    Updating the "app/config/parameters.yml" file
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache
    Clearing the cache for the dev environment with debug true
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installAssets declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installAssets
    Trying to install assets as symbolic links.
    Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
    The assets were installed using symbolic links.
    Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution
    The assets were installed using symbolic links.
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installRequirementsFile declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installRequirementsFile
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::removeSymfonyStandardFiles declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::removeSymfonyStandardFiles
    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::prepareDeploymentTarget declared at /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php accepts a Composer\Script\CommandEvent but post-update-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes in phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:316
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::prepareDeploymentTarget
    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$ php testing.php

    Fatal error: Cannot use lexical variable $eventName as a parameter name in /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/guzzlehttp/guzzle/src/Event/Emitter.php on line 48
    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$
  • 2018-01-08 weaverryan

    Hey Shaun!

    Ah, perfect - then the situation shouldn't be too bad! Try this:


    composer update guzzlehttp/guzzle

    Even though this isn't in your composer.json, it's required by other libs, and you should be able to update it. Let me know if it works out!

    Cheers!

  • 2018-01-08 Shaun

    Thanks for getting back to me Ryan, this is what I get:

    Shauns-MBP:Development shaunthornburgh$ cd knpu-symfony-rest-example/
    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$ composer info "guzzlehttp/guzzle"
    name : guzzlehttp/guzzle
    descrip. : Guzzle is a PHP HTTP client library and framework for building RESTful web service clients
    keywords : client, curl, framework, http, http client, rest, web service
    versions : * 5.2.0
    type : library
    license : MIT License (MIT) (OSI approved) https://spdx.org/licenses/M...
    source : [git] https://github.com/guzzle/g... 475b29ccd411f2fa8a408e64576418728c032cfa
    dist : [zip] https://api.github.com/repo... 475b29ccd411f2fa8a408e64576418728c032cfa
    names : guzzlehttp/guzzle

    autoload
    psr-4
    GuzzleHttp\ => src/

    requires
    guzzlehttp/ringphp ~1.0
    php >=5.4.0

    requires (dev)
    ext-curl *
    phpunit/phpunit ~4.0
    psr/log ~1.0
    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$ composer info "guzzle/guzzle"

    [InvalidArgumentException]
    Package guzzle/guzzle not found

    show [--all] [-i|--installed] [-p|--platform] [-a|--available] [-s|--self] [-N|--name-only] [-P|--path] [-t|--tree] [-l|--latest] [-o|--outdated] [-m|--minor-only] [-D|--direct] [--strict] [-f|--format FORMAT] [--] [<package>] [<version>]

    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$

  • 2018-01-08 weaverryan

    Hey somecallmetim27 !

    Sorry about the issues :/. It's something we think a lot about - we don't want to take down tutorials as they age, but, little-by-little, they start having problems. We're also thinking about the virtual env / docker setup. It's something we'll discuss for 2018 for sure!

    However, this tutorial should *not* be a problem, but it looks like we need to make one tweak! To fix this issue, try:


    composer update doctrine/annotations

    The tutorial ships with version 1.2.4, and this bug was fixed in 1.2.5. Sheesh, bad luck :). Let me know if that works! I'm going to update the code download now to use that new version.

    Cheers!

  • 2018-01-08 weaverryan

    Hey Shaun!

    Hmm, interesting! I wonder if the Guzzle bug also exists in the older version that we're using in this tutorial. What do you see when you run these 2 commands?

    ```
    composer info "guzzlehttp/guzzle"
    composer info "guzzle/guzzle"
    ```

    We'll figure this out! If there *is* an old bug with the version of Guzzle we're using in this tutorial, then unfortunately, the best solution will be to use a newer version of Guzzle. That's only annoying because it means that code in this tutorial won't work exactly :/. The code in this tutorial is a few years old now, so it's starting to show its age (but the explanations and philosophies we teach are still very good).

    Cheers!

  • 2018-01-07 somecallmetim27

    I gave up on the previous tutorial on REST here using Silex because the tutorial is ancient and it felt like I was running into a lot of compatibility issues due to the project being so old. I ended up finding another tutorial elsewhere and then coming back here for the next version of REST.

    The trouble is now I seem to be running into the same problems here. When I googled a problem I was having with set up today, I found the following answer on stack overflow.

    "The older version of Annotations is referring to the opcache.load_comments setting in php.ini, which does not exist in PHP 7:"

    Isn't there a way to set up a virtual env that can be easily cloned so everyone is starting in the same place? If not, maybe tutorials should be pulled down after they reach a certain age.

  • 2018-01-06 Shaun

    Thanks @Ryan and @Victor, how can I update the Guzzle version if it's not in composer.json ?

    I tried:

    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$ composer update vendor/guzzlehttp:5.3.1
    Package "vendor/guzzlehttp:5.3.1" listed for update is not installed. Ignoring.
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Nothing to install or update

  • 2018-01-04 Victor Bocharsky

    Hey Shaun,

    You can see a lot of information by executing "composer info package_name" command, e.g. to see the version of guzzlehttp/guzzle, run:

    composer info "guzzlehttp/guzzle"

    Cheers!

  • 2018-01-03 Shaun

    Thanks Ryan, I'm not sure how I can check as I don't see it in my composer.json...

  • 2018-01-03 weaverryan

    Hey Shaun!

    Ah, unfortunately, it looks like a bug in Guzzle and PHP 7! https://github.com/guzzle/g....

    What version of Guzzle are you using? I believe this bug was fixed in 5.3.1 (and was not present in 6).

    Cheers!

  • 2018-01-02 Shaun

    When I try to run testing.php I get the following error:

    Shauns-MBP:knpu-symfony-rest-example shaunthornburgh$ php testing.php

    Fatal error: Cannot use lexical variable $eventName as a parameter name in /Users/shaunthornburgh/Documents/Development/knpu-symfony-rest-example/vendor/guzzlehttp/guzzle/src/Event/Emitter.php on line 48

  • 2017-11-14 Diego Aguiar

    Hey Mykel Chang

    As Victor said, you should base your decisions thinking about your clients (API consumers), so if you can get feedback from them, it will help you a lot in taking those decisions :)

  • 2017-11-13 Mykel Chang

    Sometimes, it is just hard to make the choice :/

    I am trying to follow the rules as much as possible.

    I actually used the same approach in the first place (using the api/programmers path for both cases).

    But you're right, at the end it's all about the needs.

    Thanks for your answers :)

  • 2017-11-13 Victor Bocharsky

    Hey Mykel,

    Those are good question :)
    1. You can think about not API, where would you like to redirect users if they create a bunch of programmers at once? I'd probably redirect them to all programmers page. So, I'd set the location header to the all programmers URL :) But it's really up to you, you write API for *your* users, so use that URL what would be better (helpful) for them.
    2. It's OK for me until you use POST request to create those data. But if you want to allow users create both single programmers and bunch of programmers at once - I'd probably use "/api/programmers" for creating single programmers and create another endpoint to create a bunch of them at once. What about "/api/programmers/batch" URL? Or you know what, I think you can use the same "/api/programmers" endpoint, but on the server side, when you parse the request, if user send an array of arrays - create a bunch of programmers at once. But if user send only one single-level array - create a single programmer instead.

    Cheers!

  • 2017-11-10 Mykel Chang

    Hello guys o/

    I have 2 questions :

    - If I create multiple programmers at once, what should the **Location Header** mention ?
    - Is it OK to send the request on the **/api/programmers** resource to create the data ?

  • 2017-10-23 Victor Bocharsky

    Hey Potworny ,

    Thanks for sharing this solution with others!

    Cheers!

  • 2017-10-22 Potworny

    To all who had this problem:

    composer update sensio/distribution-bundle

    (I am based on the Symfony 3 code from the CHAPTER 4 (as opposed to the part 4 of this chapter)

  • 2017-10-22 Potworny

    Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
    Cannot load Zend OPcache - it was already loaded
    (...)

    And I cannot learn because the code won't work. All I can do is sit and watch :-/

  • 2017-09-19 Diego Aguiar

    Oh, I got you know. Have you tried adding `opcache.load_comments=1` to your php.ini? if it doesn't work, try updating "sensio/distribution-bundle"
    $ composer update sensio/distribution-bundle

    Cheers!

  • 2017-09-19 Tabla Mihai

    This one is :
    [Doctrine\Common\Annotations\AnnotationException]
    You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.

    Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the post-install-cmd event terminated with an exception

    [RuntimeException]
    An error occurred when executing the "'cache:clear --no-warmup'" command.

  • 2017-09-18 Diego Aguiar

    Hey Tabla Mihai

    Could you tell us what error message you are getting? So we can help you debugging

  • 2017-09-15 Tabla Mihai

    It doesn't work.
    Update to "doctrine/orm": ">2.5.0",
    "doctrine/dbal": ">=2.5-dev,<2.7-dev",

  • 2017-08-14 Diego Aguiar

    Hey Nina

    Try updating composer and then the dependency


    composer self-update
    composer update sensio/distribution-bundle

    Cheers!

  • 2017-08-13 Nina

    Hello
    When I run in cmd: composer install
    I have error

    Deprecation Notice: The callback Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap declared at C:\OpenServer\domains\start.symfony.rest\vendor\sensio\distribution-bundle\Sensio\Bundle\DistributionBundle\Composer\ScriptHandler.php accepts a Composer\Script\CommandEvent but post-install-cmd events use a Composer\Script\Event instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc... in phar://C:/OpenServer/modules/php/PHP-7.0-x64/composer.phar/src/Composer/EventDispatcher/EventDispatcher.php:316

    How can I to fix this notice?

  • 2017-07-03 Victor Bocharsky

    Hey Yan,

    > An exception occured in driver: SQLSTATE[HY000] [2002] No such file or directory

    You configure MySQL credentials incorrectly. Here's the correct parameters, you should fill them in with your correct credentials:


    // Doctrine DBAL
    $this->register(new DoctrineServiceProvider(), array(
    'db.options' => array(
    'driver' => 'pdo_mysql',
    'host' => 'localhost',
    'port' => 3306,
    'dbname' => 'rest',
    'user' => 'root',
    'password' => '',
    ),
    ));

    The "path" option is something unique for SQLite, so you don't need to use it for MySQL database.

    About schema, yeah, that's the worst part - you need to create it manually because this application do not have Symfony console support. That's why all the schema and initial data are commited to the repository, i.e. lies in the data/code_battles.sqlite file.

    Cheers!

  • 2017-06-30 Yan Yong

    Hi Victor,
    I have code_battles.sqlite, and the whole data directory got 777 permission. test.php runs without error or exception. Still not saving anything.

    so I update the configureProviders() function to use mysql. Then when I visit localhost:8000/login, I got 2002 error.
    "An exception occured in driver: SQLSTATE[HY000] [2002] No such file or directory"

    I check the database, its empty. How can I get the schema? Usually, I do that via the symfony console doctrine:schema:update, but there is no such tool.

  • 2017-06-30 Victor Bocharsky

    Hey Yan,

    I suppose if your DB credentials were invalid - you'll get a 500 error. Probably, you have permissions problem, we use SQLite for this course. The DB configuration is located in "src/KnpU/CodeBattle/Application.php" - see configureParameters() method. Make sure "data/code_battles.sqlite" file exist and have read/write permissions. Or you can simply change drive to pdo_mysql and fill MySQL credentials to use MySQL db instead.

    Cheers!

  • 2017-06-30 Yan Yong

    Hello, where this is config file for setting up the database connection? I called $this->save($programmer), but it wasn't showing up.

  • 2017-02-16 weaverryan

    Hey Igor Kalamurda!

    Good feedback! We actually don't zoom anymore on newer videos - glad that's preferable :).

    Cheers!

  • 2017-02-15 Igor Kalamurda

    please don't do zooming file view, becouse sometimes loosing and dont understand what file is edit author, so need go back to find out what file currently is

  • 2017-01-20 Henri Tompodung

    Ups.. sorry my bad! >.<
    Thank you Victor Bocharsky

  • 2017-01-20 Victor Bocharsky

    Yo Henry,

    I think it was just a misprint :) Look closely to "GuzzleHtpp" - it should be "GuzzleHttp", i.e. "GuzzleHttp\Client".

    Cheers!

  • 2017-01-20 Henri Tompodung

    Hi everyone.. I get this error:
    PHP Fatal error: Uncaught Error: Class 'GuzzleHtpp\Client' not found in /home/henri/myprojects/symfonyrest/testing.php:5
    Stack trace:
    #0 {main}
    thrown in /home/henri/myprojects/symfonyrest/testing.php on line 5

    and this is my testing.php :

    'http://localhost:8000',
    'defaults' => [
    'exceptions' => false,
    ]
    ]);

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

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

    any suggestion?

  • 2017-01-13 Ryan Pardey

    I think that might have been it. I don't think the download is buggy. I am new to Symfony, so likely user error :P

  • 2017-01-12 weaverryan

    Yo Ryan!

    Hmm, I think you're absolutely right that you need version 1.2.5 at least of doctrine/annotations to avoid this problem. But, if you download the course code, the project ships with doctrine/annotations 1.2.7. Did you possibly start the project in some other way and have an older version of the library somehow? I just want to make sure that our code download doesn't have a bug for others!

    Cheers!

  • 2017-01-10 Ryan Pardey

    I had tried that and it didn't work as I'm running php7. Ended up having to replace the Doctrine annotations directory with this:
    https://github.com/doctrine....

    And followed a few other instructions here:
    https://github.com/OfficeSt...

  • 2017-01-10 Victor Bocharsky

    Hey Ryan,

    Have you tried to add `opcache.load_comments=1` to your php.ini as it suggested?

    Cheers!

  • 2017-01-10 Ryan Pardey

    I got this on composer install:

    [Doctrine\Common\Annotations\AnnotationException]
    You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.

    Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the post-install-cmd event terminated with an exception

    [RuntimeException]
    An error occurred when executing the "'cache:clear --no-warmup'" command.

    Any ideas?

  • 2016-12-20 emm

    Thank you pointing in the right direction! Although by just changing only the nginx/sites-enabled/restexample.dev i had some troubles (404 errors) with testing env because i think the real path was "domain/app_dev.php/app_test.php/api/whatever" but Victors(knp) advice in video13/course1 helped me. i will try to manipulate paths from .htaccess from now on but so long all ok.

  • 2016-12-19 weaverryan

    Awesome! So, whether or not you need the app_dev.php is *all* about your web server :). Ultimately, if you want the "dev" environment to be executed, then the web/app_dev.php must be executed. As I mentioned earlier, if you use the built-in web server with Symfony, it pre-configures things so that you *don't* need the app_dev.php at the beginning of all of the URLs: if there is no file in the URL, then it is "rewritten" to use app_dev.php. If you use Apache, for example, you could setup rewrite rules locally to do the same. In fact, there is a web/.htaccess file that sets up rewrite rules in Apache so that app.php is used by default (which is what you want in production). I know a lot of people that setup their web server locally to automatically rewrite to app_dev.php so that they don't need to have it in their URL.

    Cheers!

  • 2016-12-18 emm

    That helped me!! i couldn't navigate the website so i changed nginx to serve app_dev.php and its working. But isn't there a way to prepend all routes with /app_dev.php within symfony?

    thanks!

  • 2016-10-06 weaverryan

    Hey Maksym!

    We have it because it's still relevant (the changes from Symfony 2 to 3 - other than the directory structure changes - are quite minor). We have a few other things on the Symfony 3 track that are done with later versions of Symfony 2, but are still relevant. And in this case, we *do* switch to Symfony 3 during Course 4 - so the series is a bit mixed!

    Cheers!

  • 2016-10-06 Maksym Minenko

    Why is this on the Symfony *3* track, I wonder?

  • 2016-09-07 Victor Bocharsky

    Hey Prakash,

    Everything is mostly the same. First of all you need to upgrade composer dependencies according to the Symfony 3. To do that, you should ensure that bundles you use in project support Symfony 3.x, check its composer.json file to see symfony/framework-bundle: ^2.0 || ^3.0. If you see "^3.0" - it should work and you can do upgrade.
    P.S. Don't forget to fix all deprecations in 2.8 before upgrading. We have a tutorial about it:
    How to Upgrade to Symfony 2.8, then 3.0
    .

    Cheers!

  • 2016-09-06 Prakash

    Hello how i do the REST API with syfmony 3.* version. currently example is with symfony 2.6. how i can migtae to syfmony 3?

  • 2016-08-23 weaverryan

    Hey Andjii!

    The tutorial uses guzzle/guzzle version 3.7, not guzzlehttp/guzzle at version 6 (which I'm guessing you are using). You can use the old, guzzle/guzzle just fine, or use the new library. But in that case, you'll need to do a little bit of translating for the new version of the library. Check out this comment for a few more details, including a gist with a version of testing.php that should work with the latest version :). http://knpuniversity.com/sc...

    But, to finally answer your question, you can't just echo the $response in Guzzle 6 (as you know), but you *can* print the body:

    echo $response->getBody();

    In some ways, the new version of the library is not as user-friendly as the old version. You can also check out this Response debugging function that we use in the Symfony REST tutorial: https://gist.github.com/wea...

    Cheers!

  • 2016-08-23 Andjii

    Hello! I get the following error: Catchable fatal error: Object of class GuzzleHttp\Psr7\Response could not be converted to string in \testing.php on line 37. there is "echo $response". how should I convert my response object to string?

  • 2016-08-18 weaverryan

    Hey John!

    Yep - there are a few libraries that still work fine, but are older versions at this point. The only one that I know is actually *deprecated* is Guzzle. We use guzzle/guzzle at version 3, but the latest version os guzzlehttp/guzzle (yes it has a different name now) at version 6! We don't technically use this library for the API... but we do use it for *testing* the API. In the Symfony REST tutorial - starting on episode 4 (http://knpuniversity.com/sc... we use the latest and greatest version 6. So, if you're curious about the differences, you can compare. Otherwise, if you're here for the API stuff, I wouldn't worry about it :).

    About the 404 on the Font file, I'm not sure about that. If you download the starting code, there *is* a web/vendor/font-awesome/fonts/fontawesome-webfont.ttf file in the project - so it should be loading this just fine. It shouldn't affect anything either way (you might just be missing some cute icons) but the 404 is mysterious!

    Cheers!

  • 2016-08-17 John

    When I did a composer install, I did receive a ton of deprecation notices. But the app runs except in the browser console it says GET http://localhost:8000/vendor/font-awesome/fonts/fontawesome-webfont.ttf?v=4.0.3 404 (Not Found)

  • 2016-06-09 weaverryan

    Good find Vlad! That is a pretty subtle difference between the old and new version of Guzzle. Thanks for sharing!

  • 2016-06-08 Vlad

    Thanks Ryan! Changing base_url to base_uri fixed the issue for me. I'm using Guzzle 6.

  • 2016-05-18 weaverryan

    Nice catch!

  • 2016-05-18 weaverryan

    Hi Roberto!

    Actually, I would argue that testing.php is not better than Postman :). However, we will soon (in the next chapters in this tutorial) take the code from testing.php and turn them into true functional tests for our API. I believe this *is* better than Postman, because we now have a test suite that we can run at any time.

    Cheers!

  • 2016-05-16 Thierno Diop

    oki its good it was because in scurity.yml the A in api was writen in capital

  • 2016-05-16 Thierno Diop

    when i execute the testing.php file i am redirected to the login page is it normal ?

  • 2016-05-13 Roberto Briones Argüelles

    Why is better option to use the `testing.php` file instead Postman? Seems dirty to me.

  • 2016-03-24 cool

    im sorry but i have to say this, her voice is too sweet for the tutorial. it almost put me to sleep

  • 2016-03-16 weaverryan

    Hi Scott!

    Because you're using Symfony, if you use the built-in web server command from Symfony (php app/console server:run) it will start a built-in web server where it *defaults* to using app_dev.php - without you needing to have it in the URL. Alternatively, you can setup any other web server locally to have the same behavior.

    BUT, more generally, we do talk about this in our Symfony REST tutorial. In our tests, we use a hook into Guzzle that automatically prepends the URL: http://knpuniversity.com/sc.... In episode 4 (which I'm recording right now), I've updated the code that hooks into Guzzle to work for Guzzle version 6: http://knpuniversity.com/sc...

    I hope that helps - good question!

  • 2016-03-15 Scottie Gutman

    In my testing.php i had to make the line $response = $client->post('/app_dev.php/api/programmers'); How do I change that?

  • 2016-01-17 Shairyar Baig

    Hi, I ended up using the version mentioned in tutorial tip and that fixed the problem.

  • 2016-01-16 weaverryan

    Hey Shairyar!

    Ah, the infamous "malformed URL" error - I get this a lot in my day-to-day work. Usually, it's because I've misconfigured the "base_uri" setting - so that when I request "/api/programmers", it literally tries to request this URI, instead of http://localhost:8000/api/programmers. Double check how you're setting the base_uri for whatever Guzzle version you're on. In Guzzle 5, I think the setting was base_url, and in 6, it's base_uri. Guzzle has been a tricky library lately, as many things have changed quickly.

    Cheers!

  • 2016-01-14 Shairyar Baig

    I am stuck at the same problem and the updated testing.php does not work either, if it helps this is the full error I am getting


    PHP Fatal error: Uncaught exception 'GuzzleHttp\Ring\Exception\RingException' with message 'cURL error 3: <url> malformed' in /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php:127
    Stack trace:
    #0 /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php(91): GuzzleHttp\Ring\Client\CurlFactory::createErrorResponse(Array, Array, Array)
    #1 /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php(96): GuzzleHttp\Ring\Client\CurlFactory::createResponse(Array, Array, Array, Array, Resource id #78)
    #2 /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php(68): GuzzleHttp\Ring\Client\CurlHandler->_invokeAsArray(Array)
    #3 /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/Middleware.php(54): GuzzleHttp\Ring\Client\CurlHandler->__invoke(Array)
    #4 /Users/shairyar/Sites/REST/vendor/guzzlehttp/ringphp/src/Client/Middleware.php(30): GuzzleHttp\Ring\Client\Middleware::GuzzleHttp\Ring\Client\{closure}(Array)
    #5 /Users/shairyar/Sit in /Users/shairyar/Sites/REST/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 51

    Fatal error: Uncaught exception 'GuzzleHttp\Ring\Exception\RingException' with message 'cURL error 3: <url> malformed' in /Users/shairyar/Sites/REST/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 51

    GuzzleHttp\Exception\RequestException: cURL error 3: <url> malformed in /Users/shairyar/Sites/REST/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 51

    Call Stack:
    0.0001 225136 1. {main}() /Users/shairyar/Sites/REST/testing.php:0
    0.0071 1314064 2. GuzzleHttp\Client->post() /Users/shairyar/Sites/REST/testing.php:21
    0.0092 1683720 3. GuzzleHttp\Client->send() /Users/shairyar/Sites/REST/vendor/guzzlehttp/guzzle/src/Client.php:150

  • 2015-11-17 Noah Glaser

    I did the http_errors is false and it worked for me. Thank you
    'http_errors' => false

  • 2015-10-09 weaverryan

    Yo Azeem!

    I think you may be using a newer version of Guzzle than I am in the tutorial - I'm using 3.7 (which is quite old at this time). If you use something newer, you'll just need to translate a bit of the code. The full testing.php for the newest version of Guzzle would look something like this: https://gist.github.com/wea.... Warning - I just hacked that together, it's probably not 100% perfect :).

    Or, you can use the 3.7 version - definitely the way to go if you just want to code through and learn the REST stuff.

    Cheers!

  • 2015-10-08 Azeem Michael

    getting this error when I run testing.php

    Uncaught exception 'GuzzleHttp\Exception\RequestException' with message 'cURL error 3: <url> malformed (see http://curl.haxx.se/libcurl... in /Users/amichael/Public/www/justlikeme/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:187

  • 2014-10-14 weaverryan

    Hi there and thanks!

    Yes, and we actually do this a bit later - https://knpuniversity.com/s... - we don't quite have all the tools we need to do it right here.

    But technically, the spec seems unclear to me on this - I don't think you're *required* to return the resource body in the response of a 201 (after all, the Location header contains the URI to the resource). However, in practice, you *do* return the resource, because "why not" and it will likely help your API client from making an extra request.

    Cheers!

  • 2014-10-14 Natxet

    Great resource!
    Shouldn't the 201 response to a POST return also the object itself that has been created?