This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
Buy Access to Course
04.

Bundle Service Class

|

Share this awesome video!

|

Keep on Learning!

Ok, our bundle is installed and ready to roll. Time to add some functionality by adding our first service class. This will be the star of the show. Our bundle is for translating objects, so this seems like the place to start.

In our bundle's src directory, create a new PHP class: ObjectTranslator.

ObjectTranslator

First, mark this class as final. This isn't meant to be extended. When developing bundles, it's important to be explicit about your class design and their intentions. This makes it easy to keep backwards compatibility. Removing final later isn't a breaking change, but adding final is. We'll explore more of these tricks as we go along:

// ... lines 1 - 4
final class ObjectTranslator
{
// ... lines 7 - 17
}

Create a method: public function translate(object $object). Return type: object. This will eventually house the logic for translating objects. For now, just return the passed $object:

// ... lines 1 - 4
final class ObjectTranslator
{
// ... lines 7 - 13
public function translate(object $object): object
{
return $object;
}
}

Our service needs a constructor to inject a few goodies. Add public function __construct(private LocaleAwareInterface $localeAware, private string $defaultLocale). We need the LocaleAwareInterface service to get the current locale of the request, and we'll also need our app's default locale:

// ... lines 1 - 6
final class ObjectTranslator
{
public function __construct(
private LocaleAwareInterface $localeAware,
private string $defaultLocale,
) {
}
// ... lines 14 - 33
}

Down in translate(), we can add some easy logic. Grab the current locale with $locale = $this->localeAware->getLocale(). Now, if the current locale is the same as the default locale, we don't need to do any translating, so add an if ($this->defaultLocale === $locale) and just return the raw object in this case:

// ... lines 1 - 6
final class ObjectTranslator
{
// ... lines 9 - 21
public function translate(object $object): object
{
$locale = $this->localeAware->getLocale();
if ($this->defaultLocale === $locale) {
return $object;
}
// ... lines 29 - 32
}
}

Below is where we'll eventually add the real translation logic, but just add a comment for now: todo translate object:

// ... lines 1 - 6
final class ObjectTranslator
{
// ... lines 9 - 21
public function translate(object $object): object
{
// ... lines 24 - 29
// todo translate object
return $object;
}
}

Let's use this new service in ArticleController::show(). Expand this method a bit and inject it: ObjectTranslator $translator:

38 lines | src/Controller/ArticleController.php
// ... lines 1 - 11
final class ArticleController extends AbstractController
{
// ... lines 14 - 22
#[Route('/news/{slug:article}', name: 'app_article_show')]
public function show(Article $article, ObjectTranslator $translator): Response
// ... lines 25 - 36
}

Run the injected Article through our new service: $article = $translator->translate($article):

38 lines | src/Controller/ArticleController.php
// ... lines 1 - 11
final class ArticleController extends AbstractController
{
// ... lines 14 - 23
public function show(Article $article, ObjectTranslator $translator): Response
{
$article = $translator->translate($article);
// ... lines 27 - 35
}
}

Sweet!

PHP Generics

Notice if we try and access a method on $article before running through our service, PhpStorm can auto-complete all the methods on $article. But... if we try and access a method on $article after running it through $translator->translate(), we don't have auto-completion. PhpStorm has no idea what $article is now - just that it's "an object". This is a drag... But we can fix this with PHP generics!

Generics are a way to provide additional type information to our editor.

Check this out: above ObjectTranslator::translate(), generate some doc blocks. This just matched the method signature and isn't super helpful... so add @template T of object above. This declares a template type T that must be an object. T is like an alias, or placeholder and can be any string.

Now, for @param and @return, replace object with T:

// ... lines 1 - 6
final class ObjectTranslator
{
// ... lines 9 - 14
/**
* @template T of object
*
* @param T $object
*
* @return T
*/
public function translate(object $object): object
// ... lines 23 - 33
}

This tells our editor: "Whatever object type is passed to this method, the return type will be the same object type."

Back in ArticleController::show(), after we call translate(), try auto-completing again on $article. Boom! PhpStorm knows exactly what $article is now. I love this!

Remove that extra code - the translated article is now passed to our template so our work here is done.

Jump back to our browser and visit an article page... An error... "Cannot autowire argument $translator..."

Symfony doesn't know about our bundle's service - it's just a plain PHP class still...

Let's fix that next!