This tutorial has a new version, check it out!

Creating a Service Class

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Ready to move a chunk of code out of the controller? Well good for you.

Step 1: create a new PHP class. In AppBundle, I'll create a new directory called Service - but that could be called anything. Inside, add a new PHP class called MarkdownTransformer:

... lines 1 - 2
namespace AppBundle\Service;
class MarkdownTransformer
{
... lines 7 - 10
}

If you're keeping score at home, that could also be called anything.

Start this with one public function parse() with a $str argument:

... lines 1 - 4
class MarkdownTransformer
{
public function parse($str)
{
return strtoupper($str);
}
}

Eventually, this will do all the dirty work of markdown parsing and caching. But for now... keep it simple and return strtoupper($str). But use your imagination - pretend like it totally is awesome and is parsing our markdown. In fact, it's so awesome that we want to use it in our controller. How?

Find GenusController. First, create a new object with $transformer = new MarkdownTransformer():

... lines 1 - 13
class GenusController extends Controller
{
... lines 16 - 58
public function showAction($genusName)
{
$em = $this->getDoctrine()->getManager();
$genus = $em->getRepository('AppBundle:Genus')
->findOneBy(['name' => $genusName]);
if (!$genus) {
throw $this->createNotFoundException('genus not found');
}
$markdownParser = new MarkdownTransformer();
... lines 71 - 97
}
... lines 99 - 123
}

Noooothing special here: the new method is purposefully not static, and this means we need to instantiate the object first. Next, add $funFact = $transformer->parse() and pass $genus->getFunFact():

... lines 1 - 69
$markdownParser = new MarkdownTransformer();
$funFact = $markdownParser->parse($genus->getFunFact());
... lines 72 - 125

And that's it! If you're feeling massively underwhelmed... you're right where I want you! I want this to be boring and easy - there are fireworks and exciting stuff later.

Finish this by passing $funFact into the template so we can render the parsed version:

... lines 1 - 13
class GenusController extends Controller
{
... lines 16 - 58
public function showAction($genusName)
{
... lines 61 - 69
$markdownParser = new MarkdownTransformer();
$funFact = $markdownParser->parse($genus->getFunFact());
... lines 72 - 92
return $this->render('genus/show.html.twig', array(
... line 94
'funFact' => $funFact,
... line 96
));
}
... lines 99 - 123
}

Then, open the template and replace genus.funFact with just funFact:

... lines 1 - 4
{% block body %}
<h2 class="genus-name">{{ genus.name }}</h2>
<div class="sea-creature-container">
<div class="genus-photo"></div>
<div class="genus-details">
<dl class="genus-details-list">
... lines 12 - 15
<dt>Fun Fact:</dt>
<dd>{{ funFact }}</dd>
... lines 18 - 19
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 25 - 42

Try it out: open up localhost:8000/genus - then click one of the genuses. Yes! The fun fact is screaming at us in upper case.

So believe it or not: you just saw one of the most important and commonly-confusing object-oriented strategies that exist anywhere... in any language! And it's this: you should take chunks of code that do things and move them into an outside function in an outside class. That's it.

Oh, and guess what? MarkdownTransformer is a service. Because remember, a service is just a class that does work for us. And when you isolate a lot of your code into these service classes, you start to build what's called a "service-oriented architecture". OooOOoooOOOo. That basically means that instead of having all of your code in big controllers, you organize them into nice little services that each do one job.

Of course, the MarkdownTransformer service isn't actually transforming... any... markdown - so let's fix that.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}