SymfonyCasts
@symfonycasts.com
680 followers 5 following 140 posts
Injecting Unicorns and Rainbows back into learning PHP // The official way to learn Symfony: http://SymfonyCasts.com
Posts Media Videos Starter Packs
symfonycasts.com
Our bundle could use a Twig filter. Let's add one and use a Twig runtime to keep things snappy! Oh, and shhh... we'll look at hidden services too. 🕵️‍♀️
`translate_object` Twig Filter
We have our "ObjectTranslator" service fully working. It can be injected and used in PHP code like we're doing in "ArticleController::show()"
symfonycasts.com
symfonycasts.com
Our translations work 🥳 but relying on the idea that all entities will have a getId() method? Kinda risky. Let’s make it rock-solid by letting Doctrine handle ID fetching - no hoping and wishing needed!
Fetch the Object ID with Doctrine
Currently, our French translation data is being successfully displayed on the page. However, our current method of fetching the ID using "$object->getId()" is a bit shaky
symfonycasts.com
symfonycasts.com
Our Setup is done! Now for the real fun, translating our objects 🎉 We’ll fetch + normalize translations with Doctrine and finally see content switch between English & French -- Oh là là!
Translation Logic
We've done a lot of setup work to get to this point, but it's time for the main event: translating our objects. Let's dive in! Our database has been loaded with these fixtures
symfonycasts.com
symfonycasts.com
We need to mark our Doctrine entities as "translatable". Sure, we could use an interface - but let’s be modern (and a little hip) by using PHP attributes instead!
Translatable Mapping
It's time to dive into how users will *mark* their app's entities for translation. For our app, we have four entities: "Article", "Category", "Tag", and "Translation"
symfonycasts.com
symfonycasts.com
We worked a bit with TDD previously, just getting a feel for it. Now it’s time to really lean in and use it in all its glory—rolling out a brand-new feature completely test-first!
TDD `TranslatedObject` Translations
The current logic of our "TranslatedObject" is solid. It effectively passes all method calls and property access to the underlying object
symfonycasts.com
symfonycasts.com
Our bundle's object wrapper doesn’t play nicely with Twig. We'll fix that - but first, let’s write a unit test to be sure it works correctly outside of Twig. We'll all sleep better with code that's properly tested!
Unit Testing `TranslatedObject`
We have a little hiccup when using our "TranslatedObject" in Twig. Here's what's going on
symfonycasts.com
symfonycasts.com
Our bundle is all about translating objects. How are we going to do this? Well, let's look at some options and implement a solution! Booyah!
Translated Object Wrapper
Alright, so now we have a way to store our translations. Let's dive into how we can make use of them
symfonycasts.com
symfonycasts.com
Our bundle’s configuration is validated and ready to go, woohoo! Now, let’s put that config to work by injecting it into one of our bundle’s services so it can actually do something useful.
Using Bundle Configuration
In our bundle, we've defined and validated the "translation_class" configuration option. In our app, we've configured it as "App\Entity\Translation"
symfonycasts.com
symfonycasts.com
Bundle configuration nodes can have validation rules. These help make sure the user’s value is what you expect - no ‘cheeseburger’ when you wanted a number. Let’s look at some out of the box rules + create a custom rule!
Bundle Configuration Validation
Alight, we have this "translation_class" configuration option in our bundle. It needs some validation: first, it should be required, second, it should be a non-empty string, and third, it should be a...
symfonycasts.com
symfonycasts.com
Our bundle needs to know about our entity class. Sounds like the perfect job for bundle configuration! We'll define a "node" and add a description + example to help our future users! Go team!
Bundle Configuration
We need to let our bundle know about our shiny new "Translation" entity. Bundle configuration is the best way to do this
symfonycasts.com
symfonycasts.com
Bundles can totally provide Doctrine entities... but there are some tricks. Let's discuss the entity we need, create the class, see some bundle-specific considerations and have a great time along the way 😎
Entities in a Bundle
Our bundle needs a way to *store* the object translations. Since we're translating Doctrine entities from the database, we know a database is in use
symfonycasts.com
symfonycasts.com
In bundles, services need to be wired manually - just like Symfony apps in 2011! But don’t worry: it’s simpler now. While we can’t use autowiring inside the bundle, we can make our bundle’s services autowireable. Nice!
"Wiring Up" our Bundle Service
We created the class we want to use as a service in our bundle, but when we try to inject it, we get this "cannot autowire argument..." error. In your end apps, you might be used to this *just working*
symfonycasts.com
symfonycasts.com
Even if you're developing bundles locally, you'll still need to install them with composer. Installation is a little different... but in a good way! We'll even stumble upon a hidden Symfony Flex feature 🌟
Install our Bundle Locally
We have a bundle "package" and bundle class, and even though it lives within our app, we still need to "install" it. This is handled with Composer like normal but the method is a little different
symfonycasts.com
symfonycasts.com
A bundle requires just one thing: a bundle class - the nerve center of your bundle. Let's create one, right after we fix a little quirk when working with multiple namespaces in the same project.
The Bundle Class
We have a home for our bundle within our app. And, we've set up it's "composer
symfonycasts.com
symfonycasts.com
🚨 New course alert: Reusable Bundles 📦! Bundle development received a facelift in Symfony 6. Let’s check this out by creating a real, usable bundle that we’ll eventually push to Packagist for all to enjoy. Step 1: Initialize our bundle!
Initialize the Bundle
Hey friends! Welcome to the *Reusable Symfony Bundle* course. I'm Kevin, and I'll be your *bundle navigator* on this journey
symfonycasts.com
symfonycasts.com
If a message fails repeatedly, instead of discarding it forever, let's send it to... the "failure transport"! Let's get this set up & learn how to find out exactly what advanced options each transport has. #Symfony symfonycasts.com/screencast/m...
The Failure Transport
We now know that each message will be retried 3 times - which is configurable - and then, if handling it *still* fails, it will be "rejected"... which is a "queue" word for: it will be removed from…
symfonycasts.com
symfonycasts.com
Time to look deeper at Messenger's retry settings & more advanced transport config. Retries have an exponential delay built-in to help overcome temporary failures. And of course, you can control all of this. #Symfony symfonycasts.com/screencast/m...
Retry Delay & Retry Strategy
By default, a message will be retried three times then lost forever. Well... in a few minutes... I'll show you how you can *avoid* even *those* messages from being lost
symfonycasts.com
symfonycasts.com
Oh no! You sent your message to a transport/queue, but when it was handled (due to a temporary connection issue) an exception was thrown! Is it lost forever? Nope - Messenger automatically retries it. Let's see retries in action! #Symfony symfonycasts.com/screencast/m...
Retrying on Failure
When you start handling things asynchronously, thinking about what happens when code fails is even *more* important! Why? Well, when you handle things synchronously, if something fails, typically,…
symfonycasts.com