gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
The MarkdownTransformer
will do two things: parse markdown and eventually cache
it. Let's start with the first.
Open up GenusController
and copy the code that originally parsed the text through
markdown. Now... paste this into the parse function, return that and update the
variable to $str
. Wow, that was easy:
... lines 1 - 4 | |
class MarkdownTransformer | |
{ | |
public function parse($str) | |
{ | |
return $this->get('markdown.parser') | |
->transform($str); | |
} | |
} |
Go back and refresh. It explodes!
Attempted to call an undefined method named "get" on MarkdownTransformer
Forget about Symfony: this makes sense. The class we just created does not have
a get()
function and it doesn't extend anything that would give this to us.
In a controller, we do have this function. But more importantly, we have access
to the container, either via that shortcut method or by saying $this->container
.
From there we can fetch any service by calling ->get()
on it.
But that's special to the controller: as soon as you're not in a controller - like
MarkdownTransformer
- you don't have access to the container, its services...
or anything.
So here's the quadrillion bitcoin question: how can we get access to the container
inside MarkdownTransformer
? But wait, we don't really need the whole container:
all we really need is the markdown parser object. So a better question is: how can
we get access to the markdown parser object inside MarkdownTransformer
?
The answer is probably the scariest word that was ever created for such a simple idea: dependency injection. Ah, ah, ah. I think someone invented that word just to be a jerk... especially because it's so simple...
Here's how it goes: whenever you're inside of a class and you need access to an object
that you don't have - like the markdown parser - add a public function __construct()
and add the object you need as an argument:
... lines 1 - 4 | |
class MarkdownTransformer | |
{ | |
... lines 7 - 8 | |
public function __construct($markdownParser) | |
{ | |
... line 11 | |
} | |
... lines 13 - 18 | |
} |
Next create a private property and in the constructor, assign that to the object:
$this->markdownParser = $markdownParser
:
... lines 1 - 4 | |
class MarkdownTransformer | |
{ | |
private $markdownParser; | |
public function __construct($markdownParser) | |
{ | |
$this->markdownParser = $markdownParser; | |
} | |
... lines 13 - 18 | |
} |
Now that the markdown parser is set on the property, use it in parse()
: get rid
of $this->get()
and just use $this->markdownParser
:
... lines 1 - 4 | |
class MarkdownTransformer | |
{ | |
... lines 7 - 13 | |
public function parse($str) | |
{ | |
return $this->markdownParser | |
->transform($str); | |
} | |
} |
We're done! Well, done with this class. You see: whoever instantiates our MarkdownTransformer
will now be forced to pass in a markdown parser object.
Of course now we broke the code in our controller. Yep, in GenusController
PhpStorm
is angry: we're missing the required $markdownParser
argument in the
new MarkdownTransformer()
call. That's cool - because now that we're in the controller,
we do have access to that object. Pass in $this->get('markdown.parser')
:
... lines 1 - 13 | |
class GenusController extends Controller | |
{ | |
... lines 16 - 58 | |
public function showAction($genusName) | |
{ | |
... lines 61 - 69 | |
$markdownTransformer = new MarkdownTransformer( | |
$this->get('markdown.parser') | |
); | |
... lines 73 - 99 | |
} | |
... lines 101 - 125 | |
} |
Try it out!
It's alive! Twig is escaping the <p>
tag - but that proves that markdown parsing
is happening. The process we just did is dependency injection. It basically says:
if an object needs something, you should pass it to that object. It's really programming 101.
But if it still feels weird, you'll see a lot more of it.
Hey 3amprogrammer!
You should get auto-completion in Twig in both cases. Well, actually, the auto-completion seems to be inconsistent in Twig - I only use the simple template name (e.g. genus/show.html.twig) and I get auto-completion *most* of the time, but not always. I'm not sure why it works sometimes, but not other times - I would love to know why! :)
Cheers!
Hey mehdi!
That's markdown! Well, at least, the markdown parser that we're using wraps all the text in a <p>
tag :).
Cheers!
Hi, when i run php app/console container:debug, most of the service id has class Names infront of them, but some of them has not, i.e service_container and request . Why this?
Hey Muhammad!
Yea, really good question! First, when a bundle adds a service to the system, it can choose to call it whatever it wants - there are no rules about what a service can be called. So, there is no technical reason for these different names. What you are seeing is am mix between some standards and some "practicality". Basically:
1) If you're creating a re-usable/shareable bundle (this includes the core bundles), it's typical a best practice to somewhat "namespace" your service ids so they don't collide with others. In other words, if I made a KnpCoolGuyBundle, then I might prefix my service ids with knp_coolguy so that they don't collide with service ids from other bundles. This is what you're seeing: service id's are basically being prefixed with a string that's similar to their namespace. But this is just a standard, there's no technical reason for this.
2) On the other hand, nobody likes long names, especially for really commonly-used things. It would be a bummer to have a service called symfony_component_dependency_injection_service_container
:). So, sometimes, a bundle will be opinionated and create a service with a very short name for convenience. This is why you have services like doctrine
, templating
or logger
.
I hope that explains it a bit! Cheers!
// 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
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"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
}
}
I have found another cool trick! I see that you render view like this:
That's okay, but If you change the string indicating the view name you will get autocompletion in twig!