When creating new Symfony projects, you can start with the bare-bones skeleton or the kitchen sink skeleton.
I've never used the kitchen sink one as I prefer to get started with the bare minimum dependencies and only add packages whenever I need them. Symfony makes it painless as, most of the time, it tells you which package to add when you are trying to use something that is not part of your set of dependencies yet.
When using the Symfony CLI to create a new project, the "small" skeleton
is used by default (which is also the recommended one in the Symfony book). This default skeleton is also a "safe" starting point as it only contains Symfony packages and no third-party ones:
1 2 3 4 5 6 7
{
"symfony/console": "*",
"symfony/dotenv": "*",
"symfony/framework-bundle": "*",
"symfony/runtime": "*",
"symfony/yaml": "*"
}
These dependencies do have some transitive dependencies though, but again, only "safe" ones. If you run symfony new test-project
today, composer show
would list 31 packages: 4 PSR packages, 4 Symfony contracts packages, 4 Symfony polyfill packages, and 19 "regular" Symfony packages. Still no "third-party" packages.
But for the vast majority of new Symfony web projects that gets created every day, we can probably imagine that they will need debugging tools like the web profiler in development, some sort of a logging package, a way to send emails, and maybe a templating engine.
That's where the website-skeleton
comes in handy. This skeleton contains way many more packages, including some you might never need. That represents 128 packages total (nice number by the way), including many third-party packages that come either from a direct Symfony package dependency or from a deep dependency of another dependency.
This skeleton was created to replace the symfony/symfony
package that nobody should use anymore. Weirdly enough, it does not include everything. The list of included packages was decided a long time ago, and some very useful packages, most of which were created since then, are still not included. While we have added the Mailer or the Notifier components when they became available, the Messenger component was never added for some reasons (probably because the use of this skeleton was never recommended anywhere).
A few months ago, there was a discussion about adding the Messenger component in the website-skeleton
list of dependencies. But instead, we decided to replace this skeleton by a pack. A pack is a "meta" Composer package: it does not provide any useful feature but only dependencies.
Meet the Webapp pack
Meet the webapp pack.
What's the benefits? The main benefit is that a pack is a regular Composer package that you can require in any existing project (even late in the game). That allows to compose your application by using more than one pack. As for code, package composition is almost always better than inheritance. The website skeleton is all about inheritance: philosophically, it extends the base skeleton. You need to use one or the other. On the contrary, the webapp pack can be included in any set of dependencies, independently of the skeleton that was used to create the project.
Think of a pack as being the equivalent of a PHP trait. Since June 2020, packs are even "better" than PHP traits as Flex automatically unpacks the dependencies for you, handing back the control to you. So, instead of depending on the webapp pack, your composer.json
contains all the dependencies listed in the pack. This allows you to remove some dependencies you don't need.
Symfony packs are not new: they have been introduced at the same time as Symfony Flex: we currently have 12 "official" packs: an ORM pack, a debug pack, a test pack, and some more.
The webapp pack is just yet another pack that you can use when creating a "traditional" web application. It includes some other packs as well. Again, it is all about composition.
As the webapp pack is nicely replacing the website skeleton, the latter is now deprecated. It is even better than that: we will only ever need one skeleton, the base one. The one with the minimal set of dependencies that are useful for any Symfony project. On top of it, mix in any number of packs.
As you can imagine, the Messenger component is part of the webapp pack.
To create a new project with the webapp pack, use:
1
$ symfony new --webapp dirname/
Which, from a Composer perspective, is more or less the equivalent of running the following commands:
1 2 3
$ composer create-project symfony/skeleton dirname/
$ cd dirname
$ composer require webapp
But this is only half of the story.
The Webapp Recipe
As you know, Symfony Flex introduced the notion of recipes associated with Composer packages. A recipe is a description of how to create a sensible default configuration for any Composer package (it can create files, modify some other ones, add environment variables, define some Docker services, ...). Whenever you add a package to your project, if a related recipe exists, it will be used to automatically configure the package in your project.
As meta packages are regular Composer packages, they can also have associated recipes. And the webapp pack does have a small recipe. It does not contain a lot, but it is a game changer when combined with other recipes.
First, it defines Doctrine as the default Messenger transport:
1 2 3 4 5
{
"env": {
"MESSENGER_TRANSPORT_DSN": "doctrine://default?auto_setup=0"
}
}
Doctrine and Messenger are part of the webapp pack, and so it makes sense to use Doctrine as the default transport for messages. Moreover, as the Doctrine recipe uses a PostgreSQL default, we can also tweak Messenger configuration accordingly.
The Messenger configuration that comes with the recipe takes all of that into account and go one step further by sending emails asynchornously by default:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async
Why is it a big deal? As one of the core team member told me once on Slack: "Do a training and ask attendees to create a new Symfony website with a form, validation, and Doctrine. You might need to wait up to an hour before seeing a 200". Ok, he was angry at that time :) But still. There is something true to this story. It's not because developers are stupid, it's because Symfony might be too complex, or too abstract, or not coherent enough when starting with the base skeleton.
Using the webapp pack allows us to automatically configure everything consistently without asking the developer to make choices. It is somewhat opinionated, but just enough to let us help you in setting up and wiring everything together.
To better understand the opinionated choices we made, here are the main ones:
- A sensible auto-generated Docker configuration that depends on what you have installed (a PostgreSQL database if you are using Doctrine, a mail catcher if you have the Mailer component installed, ...);
- PostgreSQL by default in the default Doctrine environment variable (
DATABASE_URL
), and in the Docker configuration file (docker-composer.yml
).
These opinionated choices are not related to the webapp pack as they are tied to specfic packages, but when using the webapp pack, you trigger all of them. On top of it, it unlocks the possibility to create an optimized Messenger configuration for PostgreSQL.
To sum up, when creating a new Symfony project where you know you will use Doctrine, Messenger, Mailer, Notifier, or any "more advanced" feature, the new webapp pack helps you get started faster with a great default configuration. Symfony Flex makes it painless.
Enjoy!
Nice explanation. Thanks!
I guess this isn't the correct year :D
[edit: updated to "2020"]
Great to see more opinionated decisions while making everything opt-out and re-configurable !
This is really nice.. I played around with it earlier. While not for my workflow, I see how this can be a great starting point for those starting out.
this is really nice. website-skeleton as a flex mega-pack.
It's really cool this new pack, since doctrine is used by default as a transport for Messenger, I think the package << symfony/doctrine-messenger >> should also be directly part of the pack to avoid throwing an exception.
it's really cool this new pack, since doctrine is used by default as a transport for Messenger, I think the package " symfony/doctrine-messenger " should also be directly part of the pack to avoid throwing an exception
This is great ! 👏 It would even be better if somehow we could interactively choose between a PostgreSQL and MySQL database
Just added symfony/doctrine-messenger indeed, it was an oversight. Thank you.
nice trick 👏
Great, thanks!