gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Hey guys! Finally part 5 - and it's special! It's my chance to take on some buzzwords directly: like hypermedia & HATEOAS.
Now, defining these terms is easy... but putting them into practice? For me, it was a disaster.
Let's take those ideas on directly in this tutorial, and clear up what's hype, and what is actually an evil ploy to prevent your awesome API from being released.
And hey, I just had an idea! You should code along with me! We've gone to all this trouble already to add a download for the code, so you might as well get it. Plus, there are some kitten gifs in the archive.. ok, not really. Once you've unzipped the code, move into the start/
directory, you'll have the exact code I already have here.
There's also a README file that has a few other setup instructions you'll need.
And if you've been coding along with previous courses, you're my favorite. But... download the new code: I made a few small changes to the project.
Once you're ready, start the built-in web server with:
./bin/console server:run
Go to the frontend at http://localhost:8000
and login with weaverryan
, password foo
. The API for the programmer resource is done: you can create, edit, view and do other cool programmery things.
But the real point of the site is to create epic battles, and here's how: choose a programmer, click "Start Battle", choose a project and then watch the magic. Boom!
There are three resources in play: the programmer, the project and a battle, whose entire existence is based on being related to these other two resources. And it turns out, relating things in an API can get tricky.
The resources in your API don't always need to match up with tables in your database, but they often do. Our Entity
directory holds a Programmer
entity:
... lines 1 - 9 | |
/** | |
* Programmer | |
* | |
* @ORM\Table(name="battle_programmer") | |
* @ORM\Entity(repositoryClass="AppBundle\Repository\ProgrammerRepository") | |
* @Serializer\ExclusionPolicy("all") | |
* @Link( | |
* "self", | |
* route = "api_programmers_show", | |
* params = { "nickname": "object.getNickname()" } | |
* ) | |
*/ | |
class Programmer | |
{ | |
... lines 24 - 196 | |
} |
and a Project
entity, which just has name
and difficultyLevel
fields:
... lines 1 - 6 | |
/** | |
* @ORM\Table(name="battle_project") | |
* @ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository") | |
*/ | |
class Project | |
{ | |
... lines 13 - 19 | |
/** | |
* @ORM\Column(type="string") | |
*/ | |
private $name; | |
/** | |
* 1-10 difficulty level of the project | |
* | |
* @ORM\Column(type="integer") | |
*/ | |
private $difficultyLevel; | |
... lines 31 - 55 | |
} |
Now check out Battle
: it has a ManyToOne
relationship to Programmer
and Project
:
... lines 1 - 6 | |
/** | |
* @ORM\Table(name="battle_battle") | |
* @ORM\Entity(repositoryClass="AppBundle\Repository\BattleRepository") | |
*/ | |
class Battle | |
{ | |
... lines 13 - 19 | |
/** | |
* @ORM\ManyToOne(targetEntity="Programmer") | |
* @ORM\JoinColumn(nullable=false) | |
*/ | |
private $programmer; | |
/** | |
* @ORM\ManyToOne(targetEntity="Project") | |
* @ORM\JoinColumn(nullable=false) | |
*/ | |
private $project; | |
... lines 31 - 105 | |
} |
plus a few other fields like didProgrammerWin
, foughtAt
and notes
:
... lines 1 - 6 | |
/** | |
* @ORM\Table(name="battle_battle") | |
* @ORM\Entity(repositoryClass="AppBundle\Repository\BattleRepository") | |
*/ | |
class Battle | |
{ | |
... lines 13 - 31 | |
/** | |
* @ORM\Column(type="boolean") | |
*/ | |
private $didProgrammerWin; | |
/** | |
* @ORM\Column(type="datetime") | |
*/ | |
private $foughtAt; | |
/** | |
* @ORM\Column(type="text") | |
*/ | |
private $notes; | |
... lines 46 - 105 | |
} |
Here's the goal: add an endpoint that will create a new battle resource. How should this endpoint look and act? You know the drill: let's design it first with a test.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.0.*", // v3.0.3
"doctrine/orm": "^2.5", // v2.5.4
"doctrine/doctrine-bundle": "^1.6", // 1.6.2
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // v2.10.0
"sensio/distribution-bundle": "^5.0", // v5.0.4
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.14
"incenteev/composer-parameter-handler": "~2.0", // v2.1.2
"jms/serializer-bundle": "^1.1.0", // 1.1.0
"white-october/pagerfanta-bundle": "^1.0", // v1.0.5
"lexik/jwt-authentication-bundle": "^1.4", // v1.4.3
"willdurand/hateoas-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.6
"symfony/phpunit-bridge": "^3.0", // v3.0.3
"behat/behat": "~3.1@dev", // dev-master
"behat/mink-extension": "~2.2.0", // v2.2
"behat/mink-goutte-driver": "~1.2.0", // v1.2.1
"behat/mink-selenium2-driver": "~1.3.0", // v1.3.1
"phpunit/phpunit": "~4.6.0", // 4.6.10
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}