what is trait and where should we use it in Laravel?
Trait is a feature in PHP that lets us define methods once and use them in multiple classes. for example, Laravel controllers use traits for adding capabilities like request validation or authorization.
take a look at Controller.php in Laravel:
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
AuthorizesRequests
, DispatchesJobs
and ValidatesRequests
are traits.
AuthorizesRequests has methods (like authorize()
) that we use for authorization in Laravel.
we can use this trait in a custom controller or even delete it when we don’t need any authorization in the controller.
where should we use Service classes?
Service classes are different. they are regular php classes that we define to refactor the controller.
Laravel controllers should only get and pass the requests. if we put all our logic in the controllers, maintaining the app would be difficult. That’s why some people suggest we should only handle requests in the controller and move other codes to another location.
We often see a folder named “Services” in Laravel codebases.

in the above picture, Services is in the app folder. but it doesn’t matter where we put it as long as the namespace of the classes is correct.
this is an example of a small bookshop website. I created a service class called “OrderService”. creating order items is implemented in the class:
<?php
namespace App\Services;
use App\Models\Order;
use App\Models\OrderItem;
class OrderService
{
public static function createOrder($total_amount,int $adress_id)
{
$order = new Order();
$order->user_id = Auth::id();
$order->total_amount = $total_amount;
$order->address_id = $adress_id;
$order->status = 0;
$order->ip = request()->ip();
$order->save();
return $order;
}
public static function createOrderItem(int $order_id,int $product_id,int $quantity)
{
$order_item = new OrderItem();
$order_item->order_id = $order_id;
$order_item->product_id = $product_id;
$order_item->quantity = $quantity;
$order_item->save();
return $order_item;
}
public static function createOrderGuestUser ($total_amount,array $guest_info)
{
$order = new Order();
$order->total_amount = $total_amount;
$order->first_name = $guest_info['first_name'];
$order->last_name = $guest_info['last_name'];
$order->phone = $guest_info['phone'];
$order->email = $guest_info['email'];
$order->address = $guest_info['address'];
$order->province = $guest_info['province'];
$order->city = $guest_info['city'];
$order->postcode = $guest_info['postcode'];
$order->shipping_method = $guest_info['shipping_method'];
$order->status = 0;
$order->ip = request()->ip();
$order->save();
return $order;
}
}
there are methods in this class for creating order and creating items for the order. now we just call these methods in the controller:
<?php
//orderController.php
namespace App\Http\Controllers;
class OrderController extends Controller
{
public function store()
{
$total_amount = Cart::getTotalPrice();
$current_info = session('delivery');
if (!$is_user) {
$info = [
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'phone' => request('phone'),
'email' => request('email'),
'address' => request('address'),
'province' => request('province'),
'city' => request('city'),
'postcode' => request('postcode'),
'shipping_method' => request('shipping_method'),
];
$order = OrderService::createOrderGuestUser($total_amount, $info);
foreach (Cart::getItems() as $item) {
OrderService::createOrderItem($order->id, $item['product_id'], $item['quantity']);
}
}
in this controller, I used OrderService
and Cart
. these two are service classes that handle creating orders and getting cart data from the session.
this way the controller just gets the data from the request and passes it to the service class.
can we use a trait instead of a service class?
let’s change the code to use a trait for creating order items.
first, we define a trait:
trait OrderActions{
public function createOrderItem($product_id,$quantity){
$order_item = new OrderItem();
$order_item->order_id = $this->id;
$order_item->product_id = $product_id;
$order_item->quantity = $quantity;
$order_item->save();
}
}
I created a trait to use in the Order
model. This trait has a method for creating items for the order.
in Order.php
:
//Order.php
namespace App\Models;
use App\Traits\OrderActions
class Order extends Model
{
use OrderActions;
}
and then in orderController
:
<?php
//orderController.php
namespace App\Http\Controllers;
class OrderController extends Controller
{
public function store()
{
$total_amount = Cart::getTotalPrice();
$current_info = session('delivery');
if (!$is_user) {
$info = [
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'phone' => request('phone'),
'email' => request('email'),
'address' => request('address'),
'province' => request('province'),
'city' => request('city'),
'postcode' => request('postcode'),
'shipping_method' => request('shipping_method'),
];
$order = OrderService::createOrderGuestUser($total_amount, $info);
foreach (Cart::getItems() as $item) {
$order->createOrderItem($item['product_id'],$item['quantity']);
}
}
we created items for the order with a method that was defined in the trait. it works this way too but this trait won’t have any use outside the Order model. I don’t think any class other than Order.php
would benefit from createOrderItem
method. That’s why putting it in a service class related to orders is better than just creating a trait that is only used in one class.
To summarize, it is best practice to use service classes for implementing business logic and traits for creating methods that can be used in different classes.