My typical project setup for Laravel

This is one of those posts that is more of a reference for me rather than general information. If you use PHP, Docker, MySQL, Windows, WSL2 and PHPStorm this post about my typical Laravel project setup may be of interest to you.

Docker and WSL2

I use Docker, specifically docker compose. Typically, I define two services in the docker-compose.yml file, [project_name].app and [project_name].mysql. If the project is part of a service I will define a network, this is to make it easier for containers to communicate. All the Costs to Expect Apps rely on the Costs to Expect API so they all share the same network.

Below is an example of a docker-compose.yml file for Budget, our free budgeting tool.

version: '3'
services:
    costs.budget.app:
        build:
            context: .
            dockerfile: .docker/app/Dockerfile
        image: costs.budget.app
        container_name: costs.budget.app
        ports:
            - "80:80"
        volumes:
            - .:/var/www/html
        env_file: .env
        environment:
            TZ: UTC
            DB_HOST: ${DB_HOST}
            DB_DATABASE: ${DB_DATABASE}
            DB_USERNAME: ${DB_USERNAME}
            DB_PASSWORD: ${DB_PASSWORD}
    costs.budget.mysql:
        build:
            context: .
            dockerfile: .docker/mysql/Dockerfile
        image: costs.budget.mysql
        container_name: costs.budget.mysql
        ports:
            - "3306:3306"
        env_file: .env
        environment:
            TZ: UTC
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_USER: ${DB_USERNAME}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
        volumes:
            - ./.docker/mysql/data:/var/lib/mysql
networks:
    default:
        name: costs.network
        external: true

I leave the ports at their default values. The exception is when I know I will need to run more than Docker container at a which need to communicate. I always need to run the Costs to Expect API at the same time as another Costs to Expect App so will typically map the ports on the API to 8080 and 3308.

The dockerfiles for each of the services exist in a .docker folder. I typically end up with .docker/app/Dockerfile and .docker/mysql/Dockerfile. The .docker/mysql folder will also contain a data folder, this is the volume for the MySQL data.

The MySQL Dockerfile doesn’t include any configuration, just FROM mysql:8, the example below shows a Dockerfile for a typically Laravel application.

I used to include Composer and PHPUnit but have moved away from the additional complexity as it didn’t really offer any benefits and just made configuration in PPHStorm more complicated.

FROM php:8.1-apache

COPY . /var/www/html
COPY .docker/app/vhost.conf /etc/apache2/sites-available/000-default.conf

RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd

RUN apt-get update && apt-get install -y \
    zip libzip-dev \
    && docker-php-ext-configure zip \
    && docker-php-ext-install zip

RUN docker-php-ext-install pdo_mysql bcmath

RUN chown -R www-data:www-data /var/www/html \
    && a2enmod rewrite

WORKDIR /var/www/html

PHPStorm, Composer and PHPUnit

PHPStorm works relatively flawlessly with WSL2, well it did and will again without a tweak after the 16th of January.

I store all my project files in WSL2, specifically Ubuntu, the project root for PHPStorm will normally be something like \\wsl$\Ubuntu\home\[user]\Projects\[project_name]. I have a Projects folder inside my home directory and a folder for each project. The Projects folder includes any phar files I might need so as not to duplicate them across projects.

The CLI Interpreter is set to my distro of choice, so in my case Ubuntu.

Composer settings are as defined as below.

  • Path to composer.json //wsl$/Ubuntu/home/[user]/Projects/[project_name]/composer.json
  • Execution, composer.phar
  • Composer.phar location \wsl$\Ubuntu\home\[user]\Projects\composer.phar

PHPUnit settings are defined as below

  • Remote Interpreter
  • Use composer autoloader
  • Path to script /home/[user]/Projects/[project_name]/vendor/autoload.php
  • Test runner default configuration file /home/[user]/Projects/[project_name]/phpunit.xml.dist

This Laravel project setup works well for all my projects as if I ever need to use a different version of PHP or MySQL I can update the dockerfiles for each service and rebuild. I recently needed to upgrade an App form PHP5.6 to 8.0, this setup made it easy to upgrade to each version of PHP and MySQL along the way and test at each step.

If I need to use Tailwind, SASS or other tools, I try to use them via WSL2 before going the Windows route.

After reading this you may wonder why I use Windows for development, there are three main reasons, one, I’m comfortable with Windows, two, I play games, three, I build my own PCs. The setup above will work regardless of your operating system of choice.

This post is part of a series on my development process. Please check out some of my other posts in this series, my git workflow and action class usage in my Laravel apps.