Skip to content

Project Structure

Matthew Orres edited this page Jan 27, 2019 · 2 revisions

If you intend on submitting code to the project, it would be helpful for you to have a basic understanding of how things are structured on both the backend (Silex/PHP) and the front end (Angular/Coffeescript). PHP Draft is no longer written in "your father's PHP", and was intentionally done this way to vastly improve its maintainability and increase the ease to introduce new features.

Back end (API)

Using the Silex microframework, the API is the "brain center" of the entire application. It handles security, and coordinates all data access in conjunction with said security. It does this as a mostly RESTful API.

Starting with each request made, the API is kickstarted through /api/bootstrap.php, which just pulls in all necessary Silex setup files contained in /api/config/. For brevity we will assume "it just works" is a good enough explanation for this section - requests in the form of /api/resource/id are properly routed to Controllers, and we're OK with that black box abstraction :)

Controllers are classes that are responsible for taking a web request, invoking whatever other classes are necessary to complete the request, and then formulate a response. Like in many MVC patterns, PHP Draft controllers should be very lightweight (which means no business logic should reside in a controller).

Domain (folder) is a catch-all area used to denote "business logic lives here". It's over-arching and doesn't perfectly fit in some cases, but most of the time works.

Entities are classes that are 1-to-1 representations of database tables. They contain absolutely no logic and are intentionally kept barebones. They define a class structure that DBAL (Doctrine Database Access Layer, a wrapper around PDO - which is PHP Data Objects) can use to automatically map data from SELECT queries into.

Migrations (folder) just contains all database operations in SQL to allow sites to migrate to new versions.

Models are classes that provide a go-between for business logic code and the database. This data only exists in memory.

Services are (for a lack of a better term) classes that provide a public interface to other classes for performing actions that require more than the most basic amount of business logic. Most of the time a Controller will invoke a Service in order to fulfill a request.

Validators are classes that perform data validation to ensure all data sent to the API is valid. Defining maximum field lengths or that only valid values are present on particular fields are all handled within validators.

Repositories are classes that act as an abstraction to the database. Needing to read or write to the database should always occur in a Repository, but should be kept relatively straight forward in terms of just in/out database operations. 99-100% of the API's SQL should live in these files.

Front end (Angular)

The front end of the app utilizes the AngularJS framework, which means it's a single page app (one data-heavy network load up front for speedier, more user-friendly data-only API requests for the rest of the time). index.html loads once, and then from then on the front end app asks the API to dynamically update the page from there.

The app also uses Twitter Bootstrap in order to provide a nicer more standardized user interface that leverages some really nice interface components. Bootstrap is known for allowing dead-simple responsive layouts - a single HTML file can describe where everything needs to be on-screen for both a smartphone screen (600 pixels wide) or a ultra high-end desktop monitor (2,800+ pixels wide) - and update dynamically as the screen size changes.

The app's Javascript is written in ES6 - its source is transpiled into Javascript using Babel that more browsers can understand.

Additionally, the CSS for the app is written in LESS and then processed into browser-readable CSS.

/app/features/app.coffee is the definition of the main app module, and it defines all third party module dependencies that other classes may invoke (Angular leverages dependency injection)

/app/features/config/ contains files that configure the app once, so it includes putting the JWT auth token (if present) on every request.

From here, each folder roughly represents a "feature" of the application, which also loosely maps to an AngularJS module - each module is a child of the main phpdraft module, which is the entire application. Each module is responsible for:

  1. Defining its upstream dependencies, which then signal to AngularJS to provide dependency injection for if necessary
  2. Defining the routes that its components and directives will listen for

This last part was introduced recently (ATOW) as a move from pre-AngularJS 1.5 architecture to a new 1.5+ structuring. Feature files are now stored horizontally rather than vertically - previously in order to find the template that matched a particular component, you'd have to do this odd tree traversal of up to the main, then down into the templates folder, which was tiresome and not very efficient. I blame myself for this, as I specifically had a friend and colleague named Doug that suggested I change this AS I WAS WRITING THE APP, but I was hardheaded and set in my ways at the time. Thank you Doug for suggesting I do better. I'll try better in the future :)

Clone this wiki locally