I'm currently reading about "Lean controllers"/"Business logic on services" and trying to refactor some legacy code. However I am struggling to apply what is taught in the tutorials to real code.
Online examples are simple. But in real code, I have controllers that need to use filtering, sorting, pagination (and the fact that some of these elements are part of the Eloquent query makes me even more stuck):
Legacy code
class EmployeeController extends Controller
{
public function index(Request $request)
{
$employeesQuery = Employee::with(['gender', 'birthState', 'documentType', 'maritalStatus', 'addressState', 'user']);
//filters
$filters = ModelFilterHelpers::buildFilters($request, Employee::$acceptedFilters);
$employeesQuery = $employeesQuery->AcceptRequest(Employee::$acceptedFilters)->filter();
//sort
$employeesQuery = $employeesQuery->sortable(['updated_at' => 'desc']);
//get paginate and add querystring on paginate links
$employees = $employeesQuery->paginate(10);
$employees->withQueryString();
return view('employee.index', compact('employees', 'filters'))->with('i', (request()->input('page', 1) - 1) * 10);
}
Keeping everything together within just one service function feels wrong to me and separating into other functions seems complex to me.
What I have so far is:
App\Http\Controllers\EmployeeController.php
class EmployeeController extends Controller
{
public function index(Request $request)
{
//filters
$filters = ModelFilterHelpers::buildFilters($request, Employee::$acceptedFilters);
$employees = EmployeeService::getEmployees();
return view('employee.index', compact('employees', 'filters'))->with('i', (request()->input('page', 1) - 1) * 10);
}
App\Services\EmployeeService.php
class EmployeeService
{
public static function getEmployees() : LengthAwarePaginator
{
$employeesQuery = Employee::with(['gender', 'birthState', 'documentType', 'maritalStatus', 'addressState', 'user']);
$employeesQuery = $employeesQuery->AcceptRequest(Employee::$acceptedFilters)->filter();
//sort
$employeesQuery = $employeesQuery->sortable(['updated_at' => 'desc']);
//get paginate and add querystring on paginate links
$employees = $employeesQuery->paginate(10);
return $employees->withQueryString();
}
App\Models\Employee.php (mdfst13 requested on comments)
class Employee extends Model
{
use HasFactory;
use Sortable;
use employeeFilter, Filterable;
protected $fillable = [
'name',
'email',
];
public $sortable = [
'id',
'name',
'user.email',
'created_at',
'updated_at'
];
public static $acceptedFilters = [
'name_contains',
'user_email_contains'
];
public function gender()
{
return $this->belongsTo(Gender::class);
}
public function birthState()
{
return $this->belongsTo(State::class);
}
public function documentType()
{
return $this->belongsTo(DocumentType::class);
}
public function maritalStatus()
{
return $this->belongsTo(MaritalStatus::class);
}
public function addressState()
{
return $this->belongsTo(State::class);
}
public function user()
{
return $this->hasOne(User::class);
}
So I ask:
- Is this separation correct?
- Is it okay to have filtering, sorting, pagination elements in one service method?