GitHub Repository
Session Slides
- We have a traditoional PHP app served from
public
directory. - Our website has three pages for now.
- Home page: lists all the posts from database
- About page: about page shows random about page
- Contact page: contact page shows contac details
- We use a seperate file to initialise database connection and use it only where required.
- We have also seperated the database credentials to seprate config file.
- Seperate config file helps other people to easily configure database credentials according to their own database, without messing up with actual code.
- We add this config to
.gitignore
which serves two purposes:- it changes from person to person so no need to version control it.
- it contains secret database credentials which should not be public on GitHub.
- We have the same HTML structure header, footer in all three pages.
- We have refactored the common HTML header and footer into seperate partials. It has two advantages:
- We do not need to rewrite the same HTML file again and again for new pages.
- When we add new links to the header, we only need to update in one single file.
-
We need to add users to our blog
-
We have implemented basic signup and login without any validations.
-
Signup:
- A single signup.php file checks request method, if its post request it tries to create a new user in database, else it serves the page with signup form.
- When it's a POST request we create the user in the database and login the createdt user by storing his Id in the session.
- We use php's
password_hash
function to hash their password before storing into database. It uses blowfish algorithm to hash the password. By default it uses 10 rounds of Blowfish algorithm.
-
Login:
- As we did for signup, we do the same for login. If it's a POST request we try to login user expecting credentials as POST parameters, else we serve the page with login form.
- If it's a POST request we retrive the user by their email.
- Then we match the given password with hashed password stored in database, using php's
password_verify
function. - If the password matches we login user by adding his id in the session against the key
auth
.
-
We conditionally render login/signup links or logout link depending on wether the user is logged in or not.
-
We check if Session has the key
auth
set, if its set, user must have been logged in, if its not then user is not logged in.
- We added a create post form to our home page.
- We conditionally show create post form or login message depending on wether user has logged in or not.
- The create post form submits a POST request to the
posts/store.php
script, which saves the post in database and redirects back to home page. - We check if the user is not logged in we redirect back to home without creating the post. If he is logged in we use his Id from session to assign him as the author of the post.
- We have been performing several bootstrapping tasks in the head partial, like:
- turn on error reporting
- starting/resuming session
- making database connection
- loading helper functions
- We refactor to seperate out these tasks to a seprate file
bootstrap.php
. - We use this bootstrap file in every file instead of including the whole header partial even if it is not required.
- We would be following MVC architecture, it time to sperate out the controllers, views and models.
- We build a router to route requests to our controllers.
- All the requests hit our entry point
index.php
. - Depending on the request uri and http verb (request method) our routing logic forwards the request to desired controllers.
- Each Controller is responsible for independent functionalities.
we still have views inside the controllers we would seperate them out in the next step.
- We seperate out views from controllers, and give them names with a prefix
*.view.php
, so that we can tell by name of file, that it is a view. - We only require the desired view when view is to be rendered from controller.
- We make a helper function to load views with data from a controller.
- This makes code more readable.
- We do not need to take care of the relative path to the views.
- We make helper function to redirect users to different pages.
- Again, it makes code more readable.
- Currently, we have used if-else ladder to check for every possible uri and route requests to controller.
- We refactor the logic to use a simple associative array of mappings, which maps the uri to controllers.
- Our controller are now clean, but they are still plain php scripts.
- Its time to extract into classes.
- We also group two sperate controller dealing wth same logic to different methods of the same class.
- Now, we route the request to a Controller's method. instantiate the controller and invoke the method mapped to uri.
- We are injecting database instance to every controller, through contructor.
- We can extract this common functionality to a parent class.
- We do not need to always add database instance through controller whenever we make new controller.
- In future if we need to add more dependencies we can just add them to parent class, which every controller extends.
- We have been using mysqli api to make connection with MySQL.
- Let's take a look at PDO:
- It can be used for all SQL databases.
- It has a nice object-oriented API
- It supports named-parameters
- We extend PDO to write some custom Database to simplify execution of SQL queries using prepared statements.
- Database uses the singleton pattern, it has only one instance created.
- It requires database configuration to be initialised.
- We move static routes array to a seperate file.
- In our entry point, we only load this array of routes and handle the request.
- We still have the request handling logic inside entry point.
- Lets encapsulate this into a Router class.
- Router has a nice api to register new routes instead of exposing a simple array.
- Router handles the request and route it to specific controller.
- Encapsulate request method, uri, parameters etc to a Request class.
- Request class is created from super globals
- Request object can now be passed to router which handles the request based on uri and method.
- Request is also injected to every controller method, that may need to access request parameters and other request related data.
- We have all classe that form the core of our applciation in the app folder.
- We group them altogether in a Core directory.
- We have made a query builder to abstract the SQL layer from controllers.
- Currently, Controllers know about SQL and how to query the data from database.
- We add this layer of abstraction between controller and database.
- Query builder gives us a fluent API, we do not need to care about SQL.
- More on details later.
- Encapualate the authentication logic and all autencication related helpers in the Auth class with static methods.
- We are loading each and every file we create in the entry point.
- Now, we autoload all of these classes using composer's autoloader.
- We switch to namespacing and psr-4 autoloading standard.
- PSR-4 standard needs only root namespace and from root it follows the file structure for namespacing and the autloader loads all classes in the root namespace.
- We need a dependency injection container.
- We bind the instances to application container.
- Application knows how to instantiate the dependency.
- No other part of our application is concerned about instantiating a class.
- It asks the application container for the instance.
- For a new person its hard to get the database structure.
- Migrations are the solution.
- New developer will run the migrations and it will setup all the tables in the database, required for the application to run properly.