This post begins our journey into Performance Month’s zero-to-hero project. In this part, we’ll set our project up so we can fine tune it throughout the next few posts, and bring it to a speedy perfection.
Now and then you have to create a new project repository, run that
git init command locally and kick off a new awesome project. I have to admit I like the feeling of starting something new; it’s like going on an adventure!
Lao Tzu said:
The journey of a thousand miles begins with one step
We can think about the project setup as the very first step of our thousand miles (users!) journey. We aren’t sure where exactly we are going to end up, but it will be fun!
We also should keep in mind the advice from prof. Donald Knuth:
Premature optimization is the root of all evil (or at least most of it) in programming.
Our journey towards a stable, robust, high-performance web app will start with the simple but functional application — the so-called minimum viable product (MVP). We’ll populate the database with random content, do some benchmarks and improve performance incrementally. Every article in this series will be a checkpoint on our journey!
This article will cover the basics of setting up the project and organizing files for our Symfony Flex project. I’ll also show you some tips, tricks and helper scripts I’m using for speeding up the development.
What Are We Building?
Before starting any project, you should have a clear vision of the final destination. Where are you headed? Who will be using your app and how? What are the main features you’re building? Once you have that knowledge, you can prepare your environment, third-party libraries, and dive into developing the next big thing.
In this series of articles, we’ll be building a simple image gallery blog where users can register or log in, upload images, and create simple public image galleries with descriptions written in Markdown format.
We’ll be using the new Symfony Flex and Homestead (make sure you’ve read tutorials on them, as we’re not going to cover them here). We picked Flex because Symfony 4 is just about to come out (if it hasn’t already, by the time you’re reading this), because it’s infinitely lighter than the older version and lends itself perfectly to step-by-step optimization, and it’s also the natural step in the evolution of the most popular enterprise PHP framework out there.
All the code referenced in this article is available at the GitHub repo.
We’re going to use the Twig templating engine, Symfony forms, and Doctrine ORM with UUIDs as primary keys.
Entities and routes will use annotations; we’ll have simple email/password based authentication, and we’ll prepare data fixtures to populate the database.
Getting Started with the app
To try out the example we’ve prepared, do the following:
- Set up an empty database called “blog”.
- Clone the project repository from GitHub.
- If you now open the app in your browser, you should see an exception regarding missing database tables. That’s fine, since we haven’t created any tables so far.
- Update the
.envfile in your project root directory with valid database connection string (i.e., update credentials).
- Run the database init script
./bin/refreshDb.shand wait until it generates some nice image galleries.
- Open the app in your browser and enjoy!
bin/refreshDb.sh you should be able to see the home page of our site:
You can log in to the app with credentials
email@example.com and password
123456. See LoadUserData fixture class for more details regarding generated users.
Starting from scratch
In this section, we’ll describe how to set up a new project from scratch. Feel free to take a look at the sample app codebase and see the details.
After creating a new project based on
symfony/skeleton by executing the command
composer create-project "symfony/skeleton:^3.3" multi-user-gallery-blog
… we can first set minimum stability to “dev” because of some cutting edge packages:
composer config minimum-stability dev
… and then require additional packages (some of them are referenced by their aliases, the new feature brought by Flex):
composer req annotations security orm template asset validator ramsey/uuid-doctrine
Dependencies used only in the dev environment are required with the
composer req --dev fzaninotto/faker doctrine/Doctrine-Fixtures-Bundle
Flex is doing some serious work for us behind the scenes, and most of the libraries (or bundles) are already registered and configured with good-enough defaults! Check the
config directory. You can check all the dependencies used in this project in the composer.json file.
Routes are defined by annotations, so the following will be automatically added into
controllers: resource: ../src/Controller/ type: annotation
Database, Scripts and Fixtures
DATABASE_URL environment variable (for example, by editing the
.env file) to set up a working DB connection. If you’re using our own Homestead Improved (recommended), you’ve got a database set up called
homestead with the user / pass
secret. A DB schema can be generated from existing entities by executing:
If this doesn’t run, try executing the console by invoking the PHP binary, like so:
php bin/console doctrine:schema:create
If this step executed fine in the “Getting Started with the app” section above, you should be able to see newly created tables in the database (for Gallery, Image and User entities).
If you want to drop the database schema, you can run:
./bin/console doctrine:schema:drop --full-database --force
Fake it ’til you make it!
I can’t imagine developing an app today without having data fixtures (i.e., scripts for seeding the DB). With a few simple scripts, you can populate your database with realistic content, which is useful when it comes to rapid app development and testing, but it’s also a requirement for a healthy CI pipeline.
I find the Doctrine Fixtures Bundle to be an excellent tool for handling data fixtures as it supports ordered fixtures (i.e., you can control the order of execution), sharing objects (via references) between scripts, and accessing the service container.
Default Symfony services configuration doesn’t allow public access to services, as best practice is to inject all dependencies. We’ll need some services in our fixtures, so I’m going to make all services in
AppServices publicly available by adding the following to
AppService: resource: '../src/Service/*' public: true
I’m also using Faker to get random but realistic data (names, sentences, texts, images, addresses, …).
Take a look at the script for seeding galleries with random images to get a feeling of how cool this combination is.
Usually, I combine commands for dropping the existing DB schema, creating the new DB schema, loading data fixtures, and other repetitive tasks into a single shell script — for example,
bin/refreshDb.sh — so I can easily regenerate the schema and load dummy data:
# Drop schema ./bin/console doctrine:schema:drop --full-database --force # Create schema ./bin/console doctrine:schema:create # Load fixtures ./bin/console doctrine:fixtures:load -n --fixtures src/DataFixtures/ORM # Install assets ./bin/console assets:install --symlink # Clear cache ./bin/console cache:clear
Make sure you restrict execution of this script on production, or you’re going to have some serious fun at one point.
One can argue that randomly generated data can’t reproduce different edge cases, so your CI can sometimes fail or pass depending on the data generation. It’s true, and you should make sure all edge cases are covered with your fixtures.
Every time you find an edge case causing a bug, make sure you add it to data fixtures. This will help you build a more robust system and prevent similar errors in the future.
The post Building an Image Gallery Blog with Symfony Flex: the Setup appeared first on SitePoint.