Takeaways from SymfonyLive Online German Edition 2021
I attended the SymfonyLive Online German Edition 2021 that was on the 16th of April. I didn't watch all the talks, but here are the key takeaways that I found interesting of the ones I watched.
Boring is the new hype
- Context is important. What might be the best solution for big companies like Netflix, Stripe, etc. is not necessarily for your use-case / situation
- Adding new tools to the toolbelt has a cost (build up knowledge, how to test, infrastructure, maintenance, deployment, CI integration, ...). Think about if the problem you are facing can be solved by tools you already have. If you already have a tool that provides a Good enough solution, ask yourself, is it worth using a "fancy new tool" for it?
- Learning new technologies is fun. But you are probably doing a bad job (at first at least), and creating technical debt or a suboptimal solution. Is it worth maintaing that technology in the long run?
- Symfony is a boring technology. Strict BC policy. Stable foundation: HTTP, Console, ...
- Fabien would recommend PostgresSQL over MySQL
- It acually supports Async Messages (LISTEN/NOTIFY feature). Can be used instead of something like RabbitMQ
- => One less piece of infrastructure to manage
- Also supports Locks (Advisory locks). Do you really need Redis for that? Or is Postgres good enough for your use case?
- => One less piece of infrastructure to manage
- It acually supports Async Messages (LISTEN/NOTIFY feature). Can be used instead of something like RabbitMQ
- Boring is about making safe choices
- Easy to find support
- Easier to hire people with knowledge
- Boring Infrastructure
- Do you build everything yourself? Or use SaaS instead?
- SaaS helps you focus on the topics that provide real business value
- Do you build everything yourself? Or use SaaS instead?
- Does not recommend using LTS Symfony version
- Recommendation: Upgrade every 6 months
- Fabien's ideal stack
- Currently not: Microservices on a K8s Cluster. Too complex to manage the infrastructure. Most users probably don't really have the needs for it
- A VM on a PaaS
- A Managed PostgreSQL DB
- A CDN
- Improves uptime. Probably adds a .9%
- Uses standard HTTP headers
- Fabien likes to use Cloudflare (I do as well)
- A bunch of SaaS tools
If your're interested in the topic. I'd also recommend Dan McKinley's Blog Post and Presentation about the topic.
Symfony Notifier Demystified
- Provides functionality to send notifications to recipient
- The following channels are available. These each support multiple
Transports
(except Browser)- SMS
- Twilio
- Nexmo
- Smsapi
- ...
- Chat
- Slack
- Telegram
- Discord
- Google Chat
- ...
- Browser
- Sends flash messages
- Notifications can implement a
getChannel()
method. Which defines which channel it should be sent to. - Notifications have an importance. This can be used to select which Notification should be sent to which channel.
getChannel()
overrides this if set - Channels create a channel-specific message (e.g.
SmsMessage
,EmailMessage
) from the given Notification- Channels provide interfaces (e.g.
ChatNotificationInterface
), which contain a for example aasChatMessage()
method in the Chat Channel case: Notification is able to define how a message is created
- Channels provide interfaces (e.g.
- Channels each have their own Composer package
- It also provides a Monolog handler. Allows sending Monolog logs to the Notifier which can for example send a Slack message
- Example use cases: Support requests with different possible priorities
You can find more about the Notifier Component in the Symfony Docs for it.
Hidden gems in Symfony
App\Kernel
- A full app in a single file- Can subscribe to events
- Can declare inline services by using
configureContainer()
- Allows using #Route annotations
- Use cases
- Useful in testing
- Very small Microservice
- Lambda function
- Named Autowiring Aliases
- Combine type and parameter name
- 'Symfony\Contracts\CacheInterface $arrayCache': @my_array_cache
- Combine type and parameter name
- Service Locators and Iterators
AsTaggedItem
- Command Definition
public static $defaultDescription
#[AsCommand(name: '', description: '')]
- Env Var Processors
- require
- trim
- default
- url
EnvVarLoaderInterface
- Allows you to implement your own implementation of a environment variable provider
- Is called on every request
- Example use case: High availability setup. A database instance goes down. Instantly remove it from the DB settings to route DB traffic away from the failed instance
- Process
- Wait until callback feature:
->waitUntil()
method- Allows making it event driven
- Supports "prepared" command lines
- E.g.
Process::fromShellCommandLine(ls -ls "{:$path}")
- Protection against command injection (similar to SQL injection)
- E.g.
- Wait until callback feature:
- VarDumper
- To dump in JSON api and not break the JSON: Use server dumper
- HttpClient
- Async HTTPlug client
- Uses proxies
AsyncDecoratorTrait
- Allows processing responses without breaking async
- Async HTTPlug client
- Routing
- Allow inline definition of requirements & defaults inside Annotation
stateless
Attribute. Provides error if the session is used
- Validator
NotCompromisedPassword
validatorNotCompromisedPassword
Annotation- Doctrine
#EnableAutoMapping
Annotation to fail validation if Doctrine constraints are violated
- VarExporter
- Faster than
unserialize()
- Faster than
Schrödinger's SQL - The SQL inside the Doctrine box
- Unit Of Work
- Entity Manager caches Entities. To be able to get the updated version of it, entities have to be cleared from the Entity Manager using
->clear()
- Entity Manager caches Entities. To be able to get the updated version of it, entities have to be cleared from the Entity Manager using
- Performance
- Lazy Loading
- Suboptimal performance in some use-cases.
- For example: Load entities with an array field that references another table/entity type (let's say
Foo
). Loop over these loaded entities and callgetFoo()
. Each foreach iteration will cause a separate SQL query. Not optimal
- Eager Loading
- Might hydrate entities with data that is never used. Also suboptimal
- Takeaways
- Be aware of the SQL that Doctrine executes under the hood
- Lazy Loading
- Using Doctrine does not prevent you from using plain SQL instead of DQL, where it makes sense. E.g. Window Functions, Value Lists,
JSON_OBJECT()
What's new in Doctrine 2021?
- DBAL 3
- Statement & Result are now separated
- Statement: For executing the statement
- Result: For fetching query results
- Fetch API overhauled
- More specific fetch methods replace the existing ones
- They now return a
Result
object
executeStatement()
replacesexecuteUpdate()
,exec()
&query()
. Returns count of changed rows.- Doctrine now does automatic reconnects. Automatically reconnects when DB is available again. Helps with long running tasks.
ping()
&reconnect()
is not necessary anymore. DBALException
replaced byException
- DBAL 2 is forward compatible to DBAL 3. You can already use the APIs from DBAL 3 in DBAL 2.
- Statement & Result are now separated
- ORM
- PHP 8 attributes are supported
- Typed Property Defaults: Doctrine uses PHP types by default, if defined
- Array => JSON
- Int => Int
- ...
- Support for static analyser (PHPStan, Psalm) typed arrays
toIterable()
method
- Roadmap
- Switch caching layer to PSR-6 caching
- Better Value Object DQL support
- Value Objects via nested JSON
- Enums
- ManyToAny Associations
- Dynamic Column Mapping
- Better Transaction/Unit Of Work
PHP 8
- Symfony 6 will require PHP 8
- Useful new features
get_debug_type()
str_starts_with()
,str_ends_with()
,str_contains()
- Constructor Property Promotion
throw
is now an expression. Allows usingthrow
in arrow functions, after??
or?:
, ...- => Less noisy error handling code
- Nullsafe operator for method calls
?->
match()
expression. Can be used instead ofswitch
statements- Named arguments
- Enums (PHP 8.1)
The New Testing Landscape: Panther, Foundry & More
ApiTestCase
. Specific to APIszenstruck/foundry
: Library to create data fixtures