Profile Picture
Written ByMd Tanveer

Docker with PHP, MySQL & MVC

Let us dive into the container world and create Dockerfile to build an image for PHP and apache and also create docker_compose.yml file to build mysql 5.7 image and bind all of these together. We will be following MVC pattern to make sure we cover some important aspects of configuration of apache2 server inside our docker

Coding
155
Nov 17, 2021 @ 11:23 AM

Installing docker

I am using docker on OSX Monterey (12.0.1). You can have docker install on any OS by following docker's official website

Prerequisite

Before starting installing PHP MySQL in a docker I would highly recommend to go through the "Getting Started" section of Docker which will give you the very basic knowledge (especially basic commands) of how docker works.

Project setup

Let's start with how we are going to structure our project. I am going to showcase the MVC pattern since it's most common and there is some tricks we have to follow in order to get our project running inside a docker


root directory


The root directory is docker_php. Like every MVC design pattern I have structured a simple project to add some to-dos inside a database. I have Controller, db, Model and View folders. Here I also have Dockerfile, docker-compose.yml and apache2.conf file which I will discuss later.

Controller/Controller.php


In Controller/Controller.php which loads model and view from any page. Controller/Notes.php is one of them

db/config.php


db/config.php is meant to put the constants we will be using to connect to the database, such as Hostname, DBname etc. It's always a good idea to have them separated from your main project so people without authorization do not see them.

db/DBConfig.php


db/DBConfig.php is the class where we make the connection with the database using PDO. I always prefer PDO over mysqli since you will be and should be using it for any serious app development.

Model/Model.php


Model.php is the class to manipulate the Database and pass the fetched data to Controller

View/index.php


Finally I have index.php file in the View folder to display the result on web browser. You can clone the whole project from git using below link.

Setting up image for PHP-apache

First we need to set up the image for PHP with apache server. I have used the below official PHP image from docker hub:


You do not have to download anything manually. this image will be added to the Dockerfile and will be downloaded when we will run the docker command.

Creating Dockerfile


Dockerfile is the steps that we would like to run one by one when we start our Docker container. It needs to be resided in the root directory of your project. This is how my Dockerfile looks like:

This means we would like to use php:7.2-apache image that we just saw in the docker hub.

The image does not come with PDO installed. Since we are willing to use PDO to connect to our DB so we need to install it inside the image. Notice that the command starts with "RUN". So "RUN" command will let us run any command (of course it has to be valid command) inside the image

Now this is something that I came up with in order to run my project inside Docker container. So if you set up your project like me then you will be able to run it on your computer using localhost. But I was getting the below error when I tried to run the same project from Docker:

"Forbidden
You don't have permission to access this resource.
Apache/2.4.38 (Debian) Server at localhost Port 80"

so I had to modify the apache2.conf file inside the php:7.2-apache image. But you cannot just open the image and modify a file in Docker. There are several ways to modify a file inside Docker container and I choose the one that I think is easy. So this command will remove the apache2.conf default file that we will get with the php:7.2-apache image.

And this command will copy our modified config file inside the container. Here is a good example of how to configure config file:
https://httpd.apache.org/docs/2.4/vhosts/examples.html

Then we have to copy all our files (Files inside working directory of host computer) from our working directory to the folder inside container where the apache server looks for files to execute

Finally we need to set the working directory of the container where we put our files. Now at this moment we can build our PHP apache image and try to access our app on the browser by running below commands from root directory:
docker build -t docker-php .

This will build the php apache image by following the instruction we provided in the Dockerfile. Here "-t" flag means naming the image "docker-php"
docker run -dp 80:80 docker-php:latest

"docker run" command is to run an image. Flags "-dp" or you can also put it like "-d -p" where -d means running the container in detached mode and -p means assigning host port 80 (left one 80: ) to container port 80 (right one :80 ). Now if you go to your localhost:80 you should be able to see the working directories. But you will get error if you try to access Controller/Notes.php since our database is missing. So lets create the Docker-compose file and add our DB there.

Creating Docker-compose file:

Now that we have a Dockerfile which provides the instruction on how to modify our image and build it we are going to use it to make things less clutter in our docker-compose file. Here is how the docker-compose file looks like for php:7.2-apache:

1 version: "3.7":

The first line is to declare which version of docker-compose you would like to use.
3 services:

Now we will describe our services. Services means simple the images we would like to use. There is a very important note to mention here, in order to make communication between images (in our case docker-php and mysql) all the images needs to be under same network. Hence then need to be under same service.
4 php:

Naming our docker-php to php. And all other images inside the network will recognize docker-php image as "php"
5 build:
6 context: .
7 dockerfile: Dockerfile


Then comes the build instruction where we specify the context of our project as current directory and tell the docker that we would like to build the image by following our Dockerfile that we just created. Just to remember all of these is happening in the root directory
8 depends_on:
9 - db


In a few minutes we will start building our db. Line 7 and 8 tell the docker that "docker-php" or I should say "php" will depend on other service named "db".
10 ports:
11 - 80:80


Here we make the link between host port (Left one) with the container port (right one) so we can listen to the port 80 on our local machine which is essentially listening the port 80 of our container.
12 volumes:
13 - .:/var/www/html/


volume is a crucial part of Docker. We do not want to rebuild and rerun our container ever time we make a change to our code. To solve this problem we mount our working directory into the /var/www/html/ folder of container so the changes we make in our project from host computer reflects on the /var/www/html/ folder inside container.

Building Database

We are done with the php part of our docker-compose let's build the database now. Here is steps in our docker-compose.yml file to build Database image under same network.

1 db:
2 image: mysql:5.7
3 ports:
4 - 3307:3306
5 volumes:
6 - ./db/test_add_note:/var/lib/mysql
7 environment:
8 MYSQL_ROOT_PASSWORD: root
9 MYSQL_DATABASE: test_add_note

1 db:
2 image: mysql:5.7
3 ports:
4 - 3307:3306


the first 4 lines do exactly the same as "php". We name our database image as "db" and every other images under same network will recognize it as "db". Them we create the image from mysql:5.7. Then we assign the mysql default port 3306 to our host's port 3307. Letter we will see how we can set our workbench to get our hands on the db inside container.
5 volumes:
6 - ./db/test_add_note:/var/lib/mysql


We already know the volumes let us access the data/files inside the container by creating a mount from host to container filesystem. Since we would like to manipulate our data so we have create the volume for our db as well so we can witness the change of our db state from workbench. To do so you have to create an empty folder (and keep it empty) anywhere in your project and specify the path.
7 environment:
8 MYSQL_ROOT_PASSWORD: root
9 MYSQL_DATABASE: test_add_note


Now we will set the environment variables. If you choose to set password for your db then you need to specify it here and also the schema to use.
volumes:
test_add_note:


When we run the docker-compose command the volume mapping does not happen automatically. We need to define the volume at service level and specify the mount-point.

The final docker-compose.yml file will look like this:

sudo docker-compose up

Now the time has come to run our container. We can run the above command from the root directory and you can go to your http://localhost:80 to see the files.

Setting up workbench for MySQL

You can download workbench if you do not have it already from the below link.


Once you open it click on the + sign and configure it like below image:


Once you open it click on the + sign and configure it like below image:


So the hostname would be localhost. The port would be 3307 (since our host machine's port 3307 has been assigned to container's MySQL default port 3306). Username root (if yours is root). You might want to save the password in keychain as well. Remember you need configure and connect this settings after you run the container using docker-compose up. Otherwise the connection will fail since the port 3307 starts working once your container is up and running

Now from your workbench you can run the sql script available in db/test_add_note.sql to create the schema and add 1 row. After that just go to the browser where you have localhost:80 open and refresh and go to Controller/Notes.php. You should be able to see the result as below:


1
test

Now from your workbench you can run the sql script available in db/test_add_note.sql to create the schema and add 1 row. After that just go to the browser where you have localhost:80 open and refresh and go to Controller/Notes.php. You should be able to see the result as below:


Now you can use your wokbench to add more data. Also if you stop/delete your container and restart/recreate that again you should see the same set of data read to use. And all is because of the sweet volume we have created to store all data in our host machine.
You can also check