SE Hub

Back End Development

Techniques and conventions for building and maintaining the back end of our projects.

This guide aims to provide you with a comprehensive overview of the techniques and conventions used in building and maintaining the back end of our projects.

Back End Structure

The basic back-end structure and its related files are arranged as shown. Located at app directory as below

app/
    Exceptions/         # exceptions, you can override the exception as per our standard response
    Http/
        Controllers/    # controllers, to manipulate datas and return either views or response.
            Api/        # put API controllers here
            View/       # control all  return  to  view
        Middleware/     # middleware related matters, usually used on routing things.
    Helpers/            #
    Jobs/
    Libraries/          # usually for a rather niche-specific matters (see AuditTrail).
    Models/             # for models.
    Repositories/       # use for CRUD operations only.
database/
    migrations/     # migration files for database
    seeder/         # seeder files for database

README.md           # rough documentation of this boilerplate

The reason we use Repositories so that the Controllers will have less dependencies on each other and it's more ubiquitous and manageable.

Models

  • Filename should be written in English and in singular form of the object e.g : "User"
  • It is recommended to use protected $table = 'table_name'; to explicitly declare the table name
  • It is recommended to use soft deletes for models that have high dependency connection
  • Use $dateFormat to make sure the timestamp column output remains consistent
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasFactory;
    protected $table = 'users';
}

Casting datetime

As per current timestamp form that has timezone, it is required that every model to cast timestamp attribute as the following example

protected $casts = [
        'created_at' => 'datetime:Y-m-d H:i:sO',
        'updated_at' => 'datetime:Y-m-d H:i:sO',
    ];

Repository

Below are conventions for the Model files :

  • Filename should be written in English and in singular form of the object plus Repository in singular form such as UserRepository
  • Models are strictly imported to this directory
  • CRUD operations are also put on Repository file.
  • You can create repository to cover certain case and/or scope for the better structure
<?php
namespace App\Repositories;

use App\Models\User as User;
class UserRepository {
    public function getByID($id)
    {
        return User::findOrFail($id);
    }
}

Controller

  • Filename should be written in English and in singular form of the object plus Controller in singular form e.g UserController
  • Import Repositories needed here and it is recommended to give it alias as the corresponding subject e.g
use App\Repositories\UserRepository as User;
  • Inject the repositories as this example
    protected $user;
    public function __construct(User $user)
    {
        $this->user = $user;
    }

Table Migrations

  • Table name is written in english, and having it's entity written in plural e.g
users
  • Table naming is written in snake case, try to be as descriptive while being as brief as possible. if it mention table, use the english plural form. if it mention column name, use the english singular form e.g
create_table_users
drop_table_users
alter_table_users
add_address_column
drop_address_column
alter_address_column

If by any chance the migration involves plenty of columns, it is recommended to wrap it up as alter table instead

  • Timestamp will implement timezone so we have to alter the timestamps part also add created_by and updated_by
$table->id();
$table->timestampsTz();
$table->bigInteger('created_by');
$table->bigInteger('updated_by');
  • If there is a foreign key, it is encouraged to use cascade instead of restricton regular conditions
$table->foreign('foreign_key')->references('id')->on('users')->onDelete('cascade');

APIs

This section will cover API segment for the projects.

Success Responses

The response will be represented as JSON Object. The basic response form is shown as follows

    {
        "status": 0,
        "message": "success",
        "data":[],
        "meta":[],
        "links":[],
    }

The status is 0 by default Usually message will show success except for special circumstances data will be displayed as list of object of things meta will provide information for paginations, if needed. it will be presented as follows:

"meta": {
    "pagination":{
        "total": 63,
        "current_page": 1,
        "last_page": 7,
        "on_first_page": true,
        "has_more_page": true,
        "previous_page_url": null,
        "next_page_url": "http://yoursites/api/v1/objects/list?page=2"
    },
    "links
},

links will provide any links necessary such as image links and others

Error Responses

The response will be represented as JSON Object. The basic response form is shown as follows

    {
        "status": -1,
        "message": "this is the error message",
        "data":[],
        "links":[],
    }

The status is 422 by default message will display the actual error message data will be displayed as list of error it comprised of

{
    "name":"column name"
    "message":"column error message"
}

data will have 0 to n error object

Preparing error objects

Error objects can be made by using this function

makeErrorBody(name, message)

name (short string), to identify the column or form input message (short string), to briefly identify the issue of the name attribute

How to Build the API Response

Success Response

Generate Success Response with "generic" Message

Singular object

return apiResponse([$object]);

Plural objects

return apiResponse($objects);

Generate Success Response with Special Message

Without object

return apiResponse(0,'this is a message',[],[]);

With singular object

return apiResponse(0,'this is a message',[$object],[]);

With plural objects

return apiResponse(0,'this is a message',$objects,[]);

Generate Error Response

Without object

return apiResponse(-1,'this is as error message',[],[]);

With singular object

return apiResponse(-1,'this is as error message',[$errorObject],[]);

With plural objects

return apiResponse(-1,'this is as error message',$errorObjects,[]);

the -1 on the first param is mandatory as per our standard response status for error message

Naming Conventions

  • php class PascalCase while variables and functions are written in camelCase
  • table names are written in snake_case

Request Convention

Datetime : short string in `yyyy-MM-dd` format
Timestamp : short string in `yyyy-MM-dd hh:mm:ss` format

Response and Data Type Standards and Formats

short string: 255 max character
long string: text
datetime :2021-03-10 07:24:20+0000 (laravel) 2021-03-10 09:13:19+07 (PostgreSQL)
id: bigint

Auth

This covers the login activity

Auth Request

The request example for the auth

{
	"email": "a@a.com",
	"password": "123456",
    "remember_me":true
}

make sure that the email format is correct password by default has the range of 6 to 50 characters remember_me if true will assign the remember_token as well

Auth Response

This response is critical in order to log in to the system

{
    "status": 0,
    "message": "login sukses",
    "data": [
        {
            "id": 1,
            "name": "m0WisoIzIE",
            "api_token": "cfcb6384-051c-4da7-9455-7c2c4d65fd7f"
        }
    ],
    "meta": null,
    "links": {
        "api_url": "http://127.0.0.1:8000/api/",
        "api_url_version": "http://127.0.0.1:8000/api/v1/",
        "image_url": "http://127.0.0.1:8000/storage/"
    }
}

the essentials are stored in the data. use api_token as auth header in order to access the API on header's Authorization with format Bearer [api_token]

Failed authentication response

{
    "status": -1,
    "message": "uauthenticated",
    "data": [],
    "meta": null,
    "links": {
        "api_url": "http://127.0.0.1:8000/api/",
        "api_url_version": "http://127.0.0.1:8000/api/v1/",
        "image_url": "http://127.0.0.1:8000/storage/"
    }
}

Vesperia standardization coding style

  1. General

    1. Code MUST follow all rules outlined in PSR-2.

    2. Assigning variable and function names use camelCase Style.

  2. Function style add break line after function name, add curly brackets ({}) in new line

    public function test()
    {
    	//code
    }
  3. comment style

    1. Add comments every declare function , comment can use (/****/) before function declare
      /**
        * name of function .
        * @param list of parameter clearly
        * @return what type to return .
        */
    2. add comments in function class to explain code, comment can use (//) inside function class
      public function index()
      {
          // to return index in view
          return view('index');
      }
  4. if else condition

    after if or else must be a space before curly brackets

    if (condition) {
    	//code;
    } else {
    	//code
    }

    for use curly in functions no need breakline ,

    public function index()
    {
        foreach($index as $key => value) {
            //code
        }
    }
  5. spacing

    1. use 1 space after comma

      query->where('id', '198')
      
      or
      
      return [$data, $view];
    2. use (=) to assign value to object 1 or many must be 1 space befor and after (=)

      $motor(space)=(space)$honda; $motor = $honda; [ 'motor' => $honda, 'mobil' => $yamaha, 'hp' => $ndroid ]

  6. query on laravel add breakline on condition query more than one, must be 1 tab or 1 space after first text of model

    $query = model::where('id', '100')
             	->where('nama', 'Budi')
         		->whereIn('kelas', [1, 2, 3])
         		->first();
  7. alias giving alias to import class with name of class too long with (as)

    use App\Helpers\GeneralLogHelperStatistik as log;
  8. callbackfunction use callback function to get data relation with certain conditions

    1. multiple relation every after function relation end add new line for next relation function with the query condition.

      $data = model::where('id', $foreign_key)
      			->with([
      			    'rooms' => function ($query) use ($foreign_key) {
      					// query relation without return
      				},
      			    'group' =>  => function ($query) use ($foreign_key) {
      			    	// query relation without return
      			    }
      			])
      			->get();
      
    2. single

       $data = model::whereHas('comments', function ($query) use ($text) {
       			$query->where('content', 'like', $text.'%');
       		 })->get();
  9. return null for unrelated data data that has no relation must return null

  10. use middleware XssSanitizer in routing when storing data to prevent injection string html from request

    Route::post('store', 'SomeController@store')->name('store')->middleware('xssSanitizer');
  11. restriction on every file uppload with validation mime type of file always use validation for upload file

    $this->validate($request, [
        'file' => 'mimes:jpg,png,pdf,docx,doc,xls,xlsx',
    ]);
  12. separate the function according to scope use repository for some controller every function for storing data if in one controller have 2 action storing data or query, you have to create a repository function for each function

    class someController extends Controller
    {
        public function someStore(Request $request)
        {
            $dataFunction = $this->repository->storeSomeFunction($request);
    
            $dataFunction2 =$this->repository->storeSomeFunction2($request);
    
        }
    }
  13. explanation and use of apiResponse This api response is used to return data of type json There are several ways with different responses:

    ApiResponse('message') # 1 parameter to return only message
    ApiResponse('status', 'message') # 2 parameter in first parameter to display status of response ,
                                                and second parmeter to display mesage
    ApiResponse('status', 'message', $data) # 3 parameter to return response status, message, and data
    ApiResponse('status', 'message', $data, $meta) # 4 parameter to return response status, message , data , and meta or
                                                     some additional array want to be included  in response
    ApiResponse('status', 'message', $data, $meta, $link) # 5 parameter like apiResponse 4 parameter with additional
                                                            parameters only for data links or url want to display
  14. how to use audit trail for json column define json column and data json that you want to get audit trail use convertDataJson(), first param is data json, and second param is column json what you want.

    return convertDataJson($someData, $somedata['columnJson'])
    

    this convertDataJson will return string data like normal column audit trail

  15. explanation of using library reminder notify

    - create  new file in Libraries/Reminder/Handler
    - create class someHandler to  control model reminder
    -
  16. using audit trail library

On this page