Thanks for trying out Laravel Boilerplate. I have been working on this project for more than 5 years. It is the culmination of thousands of hours of work (though it may not seem it) over these years, keeping up with all the Laravel releases and commits, merging pull requests and dealing with hundreds of issues. Along with the countless hours of reading articles and watching videos on trying to figure out the cleanest and best ways of writing the code that you see. It has and will keep changing over time and you are encouraged to make your project your own and not to feel pressured to my coding styles.
Feel free to update or improve this documentation from the GitHub repository.
This project takes up a massive amount of my spare time. I will keep trying to keep it up to date and manage the issues and pull requests, but I still work a full time job as well as freelance on nights and weekends.
This project is GitHub sponsor ready, and sponsors are always appreciated.
If you can't sponsor, a one time donation would also always be appreciated.
This project makes lite use of DDD. It is not a requirement, and you can easily refactor back to the default Laravel file structure using tools in an application like PHPStorm. For more information on domain driven design, take a look at this article, which inspired me to try it out. It is simply a way to try to organize sections of the code into folders and it may or may not be the best approach for you.
The structure of each domain is up to you, but is good practice to just follow the default Laravel app folder structure.
For example, the Auth domain has code relevant to the authentication/authorization portion of the application, and is broken down into all the relevant folders: Events/Http/Listeners/Models/etc. These are just namespaced files placed here instead of normally like App/Http/Auth it's App/Domains/Auth/Http.
Note: All current events in this project are processed with a like-named listener class that is registered in EventServiceProvider and stored in that domains Listeners directory.
Most events are used to log information using spaties activity-log package.
Some events have extra functionality:
There are many included middleware that are not Laravel default:
This method is opinionated, so refactor how you see fit.
Form requests are used where validation or authorization is needed for a request (and a policy isn't the best choice for the given request) and is injected into the controllers action signature.
Form requests are not used in Laravel's default auth scaffolding controllers where a validate() method is supplied such as login and register.
Note: Any exceptions thrown by form requests are instances of Laravel's AuthorizationException and are caught in the Exception handler which formats the response.
Models are organized by domain, and furthermore I use traits to organize each model:
Model properties such as $dates, $casts, $appends, and $with are used where necessary.
There is one observer included with the project, that is registered in the ObserverServiceProvider:
There is one rule included with the project, that is used in any request dealing with password changes:
Services are classes to attempt to extract database logic out of the controllers.
There is a BaseService class you can extend, who's constructor accepts a model and inherits some useful functionality such as get(), count(), find(), etc.
Instead of duplicating this line everywhere:
$user = User::findOrFail($id);
You can use:
$user = resolve(UserService::class)->findOrFail($id);
It's longer, but it's extracted out to one place in the codebase instead of duplicating logic everywhere. This is just an example, and a one liner usually isn't a big deal, but extracting out multi-line logic that doesn't belong in a model or elsewhere is good to have in a service dedicated to one model.
Admittedly, the exception classes could be more descriptive, and that is a goal for the next version.
But for now, there are two non-laravel exceptions that can be used:
The Exception Handler class also catches a few Laravel/Package exceptions to perform other functionality.
Though some people say it's not a good design pattern to have helper classes, I found this method easier to organize than just including a helpers.php file in composers file section.
The App\Helpers directory comes with one folder:
Any helper class placed outside of the Global folder is just a normal namespaced class that you can resolve normally.
You can find all Livewire components in the App\Http\Livewire directory.
Note: I used Livewire for the 2FA code box because if the page refreshed, the QR code would change thus invalidating the whole process. I also thought it would be cleaner than Vue since it's such small functionality.
An example of its use in the project can be found on the user/role create/edit screens of the backend as shown here and here (actual line numbers may vary if this file has been changed since the writing of this documentation).
This project comes with a few service providers that are not from a default Laravel installation:
It also changes a few default service providers:
All of the configurable items of the boilerplate can be found in the config/boilerplate.php file. Each item should have a relevant doc block.
There may be other configuration files published by default from some packages such as activitylog, permission, geoip, etc.
This project follows standard Laravel resource folder structure.
All language files for this version of the boilerplate are in JSON format except default Laravel files and package languages if published.
The SCSS files are as follows:
The views are structured much like the rest of the application, frontend/backend/etc. They usually follow the namespace structure.
There's only one folder to note, and that is components which are blade components used throughout the blade files. At the time of this writing they are all anonymous components, or they don't have an associated class and just work off of the props passed into them.
The routing is also broken into frontend and backend.
A few things to note:
Factories are supplied for all models with scopes where applicable.
Seeders are supplied for authentication and user/roles.
Some seed data only gets seeded if the application is in local/testing mode so you don't have extra data to deal with or delete in production.
Tests cover as much functionality as I could on both the frontend and backend.
The standard phpunit.xml file sets all the necessary environment variables, and runs on a sqlite database in memory that is defined in the database config file.
The BaseService is meant to be extended and a model defined in the constructor.
You may add to the BaseService class to get more functionality not included available to all of your model services.
You have no obligation to use this class if you don't want to.
Google Invisible CAPTCHA is built in to both the login and register screens using the albertcht/invisible-recaptcha package.
You can toggle them on/off using the LOGIN_CAPTCHA_STATUS and REGISTRATION_CAPTCHA_STATUS .env items.
Don't forget to add your API keys to the .env file's INVISIBLE_RECAPTCHA_SITEKEY and INVISIBLE_RECAPTCHA_SITEKEY keys.
Flash messages are defined in the includes/partials/messages blade file.
The following are available to flash to the session:
It also catches any messages from the error bag and displays in an error bootstrap alert.
You can use Laravel's view helpers to set the messages as seen in many controllers:
return view('my-view')->withFlashDanger('Something went wrong');
return view('my-view')->with('flash_danger', 'Something went wrong');
session()->flash('flash_danger', 'Something went wrong');
The user images in this project are powered by gravatar based on the users e-mail address.
You can swap this functionality out for a user uploaded image if you prefer. Just change the getAvatar method in the UserMethod class.
There is a trait included in the project that is not used on any model, but might be useful in your project:
The password rules are managed by the langleyfoxall/laravel-nist-password-rules package and the implementation can be found in the validation rules anywhere a password is created or updated.
'password' => array_merge(['max:100'], PasswordRules::register()),
You can see the full set of rules in the LangleyFoxall\LaravelNISTPasswordRules\PasswordRules class.
Permissions are managed by the spatie/laravel-permission package.
The permissions are seeded in the PermissionRoleSeeder and uses the wildcard setting.
Extra functionality has been added to the permission tables to enable permission/roles via a specific user type, parent/child permissions, as well as sorting.
When choosing roles/permissions for a new/existing user in the backend, alpine.js will decide which options are allowed for the selected user type.
The webpack file in the root works like a normal Laravel webpack file, except frontend/backend are extracted separately, and the vendor files are extracted to vendor.js.