# Code practices
# Introduction
The following sections cover a set of common programmer topics aimed a standardizing how we write code.
# Linter tools
To ensure code formatting consistency we encourage the use of the following tools:
- ESlint for Vue + JS formatting (using the settings from within each repo) -
- StyleCi for PHP formatting (external provider that runs on all commits)
To avoid conflicting linter formatting please avoid using Prettier.
# Folder structure
In order to keep the code base slightly more manageable we structure most of the code into folders that reflect the topic of the application they are associated with.
In the Laravel application these folders are applied across the folding folders in a typical Laravel app:
# For everything PHP
This applied to both knowmore and nomorePost and powerpoint-addin (see repos section):_
- \App\Models\<folder structure> (we added \Models\ to keep it a bit more clean)
- \App\Classes\<folder structure> (contains various classes not fitting within the typical Laravel MVC framework)
- \App\Https\Requests\<folder structure>
- \App\Https\Controllers\<folder structure>
The folder structure is changing and expanding on an ongoing basis. The idea is to build it around the "unifying model concepts" e.g. "Task", "Request", "Specialist", "Client", "Subproject" – as most models and features can be linked to these.
# For VUEjs components - knowmore
The high level structure is a bit different than on the PHP side of things. The idea is to build around the high-level user experiences of the different stakeholders. Thus, we have 4 main folders and a shared folder:
- Application (everything with the specialist application process)
- Onboarding (everything related to onboarding specialists)
- DashboardSpecialist (for specialists that have completed the above)
- DashboardAdmin (for project managers and any form of admins)
- Shared (keep shared components used across the others)
Within each folder we try to further group the content into folders so that it reflect "interfaces" e.g. we have folder for the "icon finder feature", and for "returning things to client" etc.
For VUE components – nomore
On the customer facing side the folder structure reflects the left side menu of the customer but also have separate folders for the onboarding process.
# Naming conventions
Note: The naming convention has changed a bit over time. Please use the below for all work going forward and feel free to adjust old code to fit accordingly.
# File names
In PHP we follow the standard Laravel naming convention. Specifics:
For classes we use starting capital and no underscore e.g. "ClassNameExampleHere.php"
For non-classes e.g. route files etc. we use camelCase e.g. "nonClassFile.blade.php"
For VUE.js files and other JavaScript files we apply all lower case and use hyphens to bind words e.g. this-is-an-example.vue
For such files we normally name by repeating the "base feature / main purpose" e.g. if we have VUE component within our icon finder interface that is responsible for presenting the search result we will call it "icon-finder-results" and not just "results" – in order to make sure it does not conflict with other "result" files across the code base.
# Method names
In general we write all method names with camelCase e.g. "createTask" or "persistData".
We aim to write them in a way in which we do not need to write documentation text to them to explain what they do e.g. "validateInput" does not require additional documentation.
Methods names should not repeat their context. We do not have to repeat the model name or feature when naming controller methods or naming private methods. E.g. we should NOT write storeSubproject on the "SubrpojectCreationController", but simply "store" as the controller name informs the reader that we are taking about subprojects.
# Model names
Models names should be written in singular e.g. "User" and NOT "Users". The associated tables should follow the default Laravel naming so that model "User" gets the table name "users".
All models are written using CaptailLetters in the start of each work as such: "ExampleOfModel"
Models names should be named so they help the reader understand the context better and to help avoid duplicate model names. Furthermore, the "base" of the model name should be derived from core object to which the model relates e.g. is model contains the security settings for a company the model name should be "CompanySecuritySettings" and not just "SecuritySettings". Other examples are:
SubprojectSpecialistApplication (when specialists apply for a given subproject)
SpecialistOnboarding (track which of the onboarding steps the specialists have been through)
For models that are purely "pivot" table or mapping tables we apply the name "Map" to the name. E.g. if we mapping a many-to-many relationship between "RatingSubproject" and "RatingSubprojectTag" we have a table called "RatingSubprojectTagMap" – which then only contains two rows: "ratingId" and "tagId"
# Database setup
The majority of the applications run on the same database as outlined above.
The database is a typical MySql database – as often used in Laravel apps.
Some of the important rules to follow when working with the database:
# Property names
As default we use camelCase for all properties (this has not been done very consistent so far – but lets try to apply this concept here). E.g. "tagId" and NOT "tag_id"
# Indexes
Apply index to properties that you can foresee will be used for "looking up" in the database as part of the feature you are working on (or later on). E.g. if we are working with "RatingSubprojects" (the model that hold ratings provided by the admin to the specialist) we know that we will be quering the database from both the "specialist point of view" e.g. "give me my ratings" and the "subrproject" point of view e.g. "what rating was received for project X" – thus we need indexes on both ids.
We should not "just" add indexes on all variables since this slows the server load whenever we store or modify a given table.
# Foreign keys
We add foreign keys whenever possible – no exceptions. There are plenty of examples in the code base to get inspiration from.
# Limit repeat information
We do not repeat data found on other table e.g. if "SubprojectRating" has a link to Subproject, and we want to show the subproject title, we do not add a title property to "SubprojectRating" table, but use relationships to get the relevant title name.
There a few exceptions for this in code base, but please apply this strictly going forward and do not change the existing case without discussing with Anders in detail first.
# Migrations for everything
We use migrations for all database modifications – no exceptions.
# Use enums where relevant
In many cases we use string variables to determine the "status" or "result" or similar aspects of a given model. In order to ensure the 100% correct spelling of any such property values to use enums as part of the migrations to enforce these.
# Model layout
For all new models we always write out the "fillable" property and determine relevant relations right away.
In order to make life easier for ourselves, we also add comments with potential enum values.
# Controllers and use cases
# Introduction to use cases
When working with controllers we apply a common term called "UseCase". What we mean by UseCase is simply a dedicated class that has the responsibility for handling a certain objection requested by the user or by another part of the application examples are "SpecialistAccountInformation" or "IconDownloadUseCase"
The UseCases are all located under \App\Classes\<folder structure>
You can thing of UseCases much similar to Laravels standard "Requests" (under App\Https\Requests"), but which might not result from a form being requested and that can be used across the app.
# Controller rules
We like to avoid having messy controllers, but on the other hand we also believe in a certain level of pragmatism.
Thus, we believe the following rules for working with controllers and UseCases:
- If a controller method has more than 30 lines it should be converted to a Request or a UseCase
- Regardless of size, if the majority of the code in a controller methods is being repeated (or will likely be repeated in the future) the relevant logic should be abstracted to a use case
- Rather more controllers than a few crowded controller. Thus, if a controller has more than 7-8 methods, you can likely split it up further
- Avoid using private methods within a controller (consider creating a trait if it really needed)
- Always add relevant middleware to the controller
- Make sure all methods on a controller has at least one test
- Make sure to clean up before you merge with the master – don't leave commented code remain when you create your push request
# Routes
We try to follow the following logic when it comes to routes:
# End-point name
We prefer to have multiple "/"s over longer name for the end-points. Example we should not write "update-test" but instead "/test/update"
We cluster routes into different files and within each file sort the routes by the controller which they "hit".
We do use the route helper methods e.g. "View" or "Redirect" where relevant, but we never write login inside the route files.