Laravel
Introduction
This is a guide to maintain a consistent and industry stand code in Laravel.
Laravel Routes
- Meaningful / Predictable
- All small letter
- 2 words must be joined with
hyphen
(-
)
This is an example of for articles
CRUD Routes
HTML listing page
GET /articles/index
Content-Type: application/xhtml+xml
Controller-Method: ArticleController@index
Model-Method: ""
Listing
GET /articles
Content-Type: application/json
Controller-Method: ArticleController@getList
Model-Method: Article::getList
Create one or many records
POST /articles
Content-Type: application/json
Controller-Method: ArticleController@createList
Model-Method: Article::createList
If an array is passed, multiple records will be created or else single record will be created.
Eg: Single Record
$inputs = [
"first_name" => 'John'
"last_name" => 'Leo'
]
Eg: Multiple Records
$inputs = [
[
"first_name" => 'John'
"last_name" => 'Leo'
],
[
"first_name" => 'David'
"last_name" => 'Leo'
]
]
Bulk Update (eg: bulk status change)
PUT /articles
PATCH /articles
Content-Type: application/json
Controller-Method: ArticleController@updateList
Model-Method: Article::updateList
Bulk Delete
DELETE /articles
Content-Type: application/json
Controller-Method: ArticleController@deteleList
Model-Method: Article::deteleList
Read a record
GET /articles/1
Content-Type: application/json
Controller-Method: ArticleController@getItem
Model-Method: Article::getItem
Update a record (update, soft delete, status change etc)
PUT /articles/1
PATCH /articles/1
Content-Type: application/json
Controller-Method: ArticleController@updateItem
Model-Method: Article::updateItem
Delete a record (hard deleted)
DELETE /articles/1
Content-Type: application/json
Controller-Method: ArticleController@deteleItem
Model-Method: Article::deteleItem
Fetch a relation of a record
GET /articles/1/relationships/author
Content-Type: application/json
Controller-Method: ArticleController@getItemAuthor
Model-Method: Article::getItemAuthor
Attach a relation to a record
POST /articles/1/relationships/author
Content-Type: application/json
Controller-Method: ArticleController@attachItemAuthor
Model-Method: Article::attachItemAuthor
Update a relation to a record
PUT /articles/1/relationships/author
PATCH /articles/1/relationships/author
Content-Type: application/json
Controller-Method: ArticleController@updateItemAuthor
Model-Method: Article::updateItemAuthor
Delete a relation to a record
DELETE /articles/1/relationships/author
Content-Type: application/json
Controller-Method: ArticleController@deleteItemAuthor
Model-Method: Article::deleteItemAuthor
Fetch list of relations
GET /articles/1/relationships/comments
Content-Type: application/json
Controller-Method: ArticleController@getItemComments
Model-Method: Article::getItemComments
Update list of relations
PUT /articles/1/relationships/comments
PATCH /articles/1/relationships/comments
Content-Type: application/json
Controller-Method: ArticleController@updateItemComments
Model-Method: Article::updateItemComments
Delete list of relations
DELETE /articles/1/relationships/comments
Content-Type: application/json
Controller-Method: ArticleController@deleteItemComments
Model-Method: Article::deleteItemComments
Include relations
GET /articles?include=comments
Content-Type: application/json
Controller-Method: ArticleController@getList
Model-Method: Article::getList
Include relation's relations
GET /articles?include=comments.created_by
Content-Type: application/json
Controller-Method: ArticleController@getList
Model-Method: Article::getList
Sorting
GET /articles?sort=title,created_by
Content-Type: application/json
Controller-Method: ArticleController@getList
Model-Method: Article::getList
Sorting in descending order of created_by
, use :desc
in front of parameter
GET /articles?sort=title,created_by:desc
Content-Type: application/json
Controller-Method: ArticleController@getList
Model-Method: Article::getList
Performance Tuning SQL Queries
SQL tuning is the process of improving SQL queries to accelerate your servers performance. It's general purpose is to reduce the amount of time it takes a user to receive a result after issuing a query, and to reduce the amount of resources used to process a query.
Big SQL queries can suffer performance issues. Performance degradation often occurs if the database is not being properly maintained or if queries can be rewritten more efficiently.
Following Query optimization tips for better performance
Apply Index On Necessary Columns:
Table indexes in databases help retrieve information faster and more efficiently. An index creates a unique data column without overlapping each other. It improves the speed of data retrieval.
Avoid Query In A Loop:
If sometimes we need to execute the same query multiple times like if we need to insert 10 records in any table at that time donโt use insert query in the loop instead of using bulk insert.
We shouldn't call Eloquent relationships method in loop. First convert model object in to collection and then pass in to loop.
Wrong Examples:
public function category()
{
return $this->belongsTo(Category::class,
'catgeory_id', 'id'
);
}
public function getList()
{
$products = Product::with(['catgory'])->all();
foreach ($products as $key => $product)
{
if($product && $product->category){
}
}
}
Correct Examples:
public function category()
{
return $this->belongsTo(Category::class,
'catgeory_id', 'id'
);
}
public function getList()
{
$products = Product::with(['catgory'])->all();
$collect_products = collect($products);
foreach ($collect_products as $key => $product)
{
if($product && $product->category){
}
}
}
Avoid too many JOINs:
When you add multiple tables to a query and join them, you may overload it. In addition, a large number of tables to retrieve data from may result in an inefficient execution plan. When generating a plan, the SQL query optimizer needs to identify how the tables are joined, in which order, how and when to apply filters and aggregation.
JOIN elimination is one of the many techniques to achieve efficient query plans. You can split a single query into several separate queries which can later be joined, and thus remove unnecessary joins, subqueries, tables, etc.
Use SELECT fields instead of retrieve all data:
The SELECT statement is used to retrieve data from the database. In the case of large databases, it is not recommended to retrieve all data because this will take more resources on querying a huge volume of data.
If we execute the following query, we will retrieve all data from the Users table, including, for example, usersโ avatar pictures. The result table will contain lots of data and will take too much memory and CPU usage.
BAD CODE
User::get();
Instead, you can specify the exact columns you need to get data from, thus, saving database resources. In this case, SQL Server will retrieve only the required data, and the query will have lower cost.
GOOD CODE
User::select('first_name','last_name','email')->get();
Use Exists instead of First or Count in condition :
Instead of using the count method to determine if any records exist that
match your query's constraints, you may use the exists
and doesntExist
methods.
It depends if you want to work with the user afterwards or only check if one exists.
If you want to use the user object if it exists:
$user = User::where('email', '=', Input::get('email'))->first();
if ($user === null) {
// user doesn't exist
}
And if you only want to check
if (User::where('email', '=', Input::get('email'))->count() > 0) {
// user found
}
Or even nicer
if (User::where('email', '=', Input::get('email'))->exists()) {
// user found
}
Query Operators
MySql Operator | Query Parameter | Purpose |
---|---|---|
= | eq | Equal to |
!= | not | Not equal to |
< | lt | Less than |
<= | lte | Less than or equal to |
> | gt | Greater than |
>= | gte | Greater than or equal to |
like | like | Like search query |
between | btw | Between |
API Routes
Get list of articles with pagination
GET /api/articles
Specify page number
GET /api/articles?page=5
Limit the results
GET /api/articles?limit=10
Sort By ascending order of title then created_by
GET /api/articles?sort=title,created_by
Sort By in ascending order of title then descending order created_by
GET /api/articles?sort=title,created_by:desc,price
Include comment in the article (relationship)
GET /api/articles?include=comments
Include comment with created_by relationship
GET /api/articles?include=comments.created_by
Examples of filters with operators
GET /api/articles?filter[is_active]=true
GET /api/articles?filter[created_by]=eq:5
GET /api/articles?filter[title]=like:hello
GET /api/articles?filter[price]=gt:5
GET /api/articles?filter[date]=btw:5|7
GET /api/articles?filter[is_active]=true&filter[created_by]=eq:5&filter[date]=btw:5|7
Multiple operators
GET /api/articles?filter[price]=gt:5|AND|lt:10
Get single record of article
GET /api/articles/1
To create single article
POST /api/articles
Update single record of article
PUT /api/articles/1
PATCH /api/articles/1
Delete single record of article
DELETE /api/articles/1
Get single record of article with it's author
GET /api/articles/1/relationships/author
Attach author (relationship) to the article
POST /api/articles/1/relationships/author
Update author (relationship)
PUT /api/articles/1/relationships/author
PATCH /api/articles/1/relationships/author
Delete author (relationship)
DELETE /api/articles/1/relationships/author
Get article with multiple comments (relationships)
GET /api/articles/1/relationships/comments
Create/Attach comments (relationships)
POST /api/articles/1/relationships/comments
Update article with multiple comments (relationships)
PUT /api/articles/1/relationships/comments
PATCH /api/articles/1/relationships/comments
Delete multiples comments (relationships)
DELETE /api/articles/1/relationships/comments
Laravel Community Guidelines for Good Coding Practices
Shorter and readable syntax
- Use shorter and more readable syntax where possible
Bad Examples
$request->session()->get('cart');
$request->input('name');
Good Example
session('cart');
$request->name;
More Examples
Common syntax | Shorter and more readable syntax |
---|---|
Session::get('cart') | session('cart') |
$request->session()->get('cart') | session('cart') |
Session::put('cart', $data) | session('cart' => $data) |
$request->input('name'), Request::get('name') | $request->name, request('name') |
return Redirect::back() | return back() |
is_null($object->relation) ? null : $object->relation->id | optional($object->relation)->id |
return view('index')->with('title', $title)->with('client', $client) | return view('index', compact('title', 'client')) |
$request->has('value') ? $request->value : 'default'; | $request->get('value', 'default') |
Carbon::now(), Carbon::today() | now(), today() |
App::make('Class') | app('Class') |
->where('column', '=', 1) | ->where('column', 1) |
->orderBy('created_at', 'desc') | ->latest() |
->orderBy('age', 'desc') | ->latest('age') |
->orderBy('created_at', 'asc') | ->oldest() |
->select('id', 'name')->get() | ->get('id', 'name') |
->first()->name | ->value('name') |
Avoid Fat Methods
- Avoid fat controllers and write frequent queries in model.
- DB related logic should be in
Eloquent models
or inRepository
/Helper
classes. - Do not include business logic in Controllers. They should be in
Repository
/Helper
classes. - Methods and Classes should adhere to the
Single Responsibility Principle
. - Don't use extensive if-else nesting. Instead, check for negative conditions first.
- Do not keep commented code in methods or classes.
Bad Examples
public static function getItem($id)
{
if(!Auth::user()->hasPermission('hrc-can-read-customers')
&& !EqCustomerUser::hasPermission(\Auth::user(),'hrc-can-read-customers')) // check Permissions
{
if(Auth::user()){
$item = self::where('id', $id)
->withTrashed()
->first();
$response['status'] = 'success';
$response['data'] = $item;
return $response;
}
else{
$response['status'] = 'failed';
$response['errors'][] = 'User not found';
return $response;
}
}
else{
$response['status'] = 'failed';
$response['errors'][] = trans("vaahcms::messages.permission_denied");
return $response;
}
// $user = Auth::user();
// if(!$user) {
// $response['status'] = 'failed';
// $response['errors'][] = 'User not found';
//
// return $response;
// }
}
Good Examples
// Add this method to Helper/Repository class
public static checkPermission($permission_slug){
if(!Auth::user()->hasPermission('hrc-can-read-customers')){
$response['status'] = 'failed';
$response['errors'][] = trans("vaahcms::messages.permission_denied");
return $response;
}
$response['status'] = 'success';
$response['messages'][] = 'Action is successful';
}
// this code will be in model
public static function getItem($id)
{
$permission_response = Helper::checkPermission('hrc-can-read-customers');
if($permission_response['status'] !== 'success') return $permission_response;
$user = Auth::user();
if(!$user) {
$response['status'] = 'failed';
$response['errors'][] = 'User not found';
return $response;
}
$item = self::where('id', $id)
->withTrashed()
->first();
$response['status'] = 'success';
$response['data'] = $item;
return $response;
}
Prefer to use Eloquent over raw SQL queries
- Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections to arrays.
- Eloquent allows you to write readable and maintainable code. Also, Eloquent has great built-in tools like soft deletes, events, scopes etc.
Bad Example
SELECT *
FROM `articles`
WHERE EXISTS (SELECT *
FROM `users`
WHERE `articles`.`user_id` = `users`.`id`
AND EXISTS (SELECT *
FROM `profiles`
WHERE `profiles`.`user_id` = `users`.`id`)
AND `users`.`deleted_at` IS NULL)
AND `verified` = '1'
AND `active` = '1'
ORDER BY `created_at` DESC
Good Example
Article::has('user.profile')->verified()->latest()->get();
- Introduction
- Laravel Routes
- CRUD Routes
- HTML listing page
- Listing
- Create one or many records
- Bulk Update (eg: bulk status change)
- Bulk Delete
- Read a record
- Update a record (update, soft delete, status change etc)
- Delete a record (hard deleted)
- Fetch a relation of a record
- Attach a relation to a record
- Update a relation to a record
- Delete a relation to a record
- Fetch list of relations
- Update list of relations
- Delete list of relations
- Include relations
- Include relation's relations
- Sorting
- Performance Tuning SQL Queries
- Query Operators
- API Routes
- Specify page number
- Limit the results
- Sort By ascending order of title then created_by
- Sort By in ascending order of title then descending order created_by
- Include comment in the article (relationship)
- Include comment with created_by relationship
- Examples of filters with operators
- Multiple operators
- Get single record of article
- To create single article
- Update single record of article
- Delete single record of article
- Get single record of article with it's author
- Attach author (relationship) to the article
- Update author (relationship)
- Delete author (relationship)
- Get article with multiple comments (relationships)
- Create/Attach comments (relationships)
- Update article with multiple comments (relationships)
- Delete multiples comments (relationships)
- Laravel Community Guidelines for Good Coding Practices