Hi all,
I have a simple expense tracking system in which a user makes a Claim and this can contain multiple Expense(s) and I made a service class to handle CRUD. Something I'm struggling with is this: as an expense can only exist in a claim, should a ClaimService be responsible rather than my existing ExpenseService?
Here's how that looks:
<?php
namespace App\Services;
use App\Models\Expense;
use App\Models\ExpenseClaim;
use App\Models\User;
class ExpenseService
{
public function retrieveExpenses(ExpenseClaim $expenseClaim)
{
return $expenseClaim->expenses()->get();
}
/**
* Create an expense within a claim.
*
* @param \App\Models\ExpenseClaim $expenseClaim
* @param \App\Models\User $user
* @param array $attributes
*
* @return App\Models\Expense
*/
public function createExpense(ExpenseClaim $expenseClaim, User $user, array $attributes)
{
return $expenseClaim->expenses()->create([
'user_id' => $user->id,
'expense_date' => $attributes['expense_date'],
'business_reason' => $attributes['business_reason'],
'category' => $attributes['category'],
'project_code' => $attributes['project_code'],
'is_foreign_expense' => $attributes['is_foreign_expense'],
'currency_code' => $attributes['currency_code'],
'conversion_rate_to_gbp' => $attributes['conversion_rate_to_gbp'],
'amount_before_conversion' => $attributes['amount_before_conversion'],
'gross_amount' => $attributes['gross_amount'],
'has_vat' => $attributes['has_vat'],
'vat_claimed' => $attributes['vat_claimed'],
]);
}
/**
* Undocumented function
*
* @param \App\Models\Expense $expense
* @param \App\Models\User $user
* @param array $attributes
*
* @return App\Models\Expense
*/
public function updateExpense(Expense $expense, User $user, array $attributes)
{
$expense->update([
'user_id' => $user->id,
'expense_date' => $attributes['expense_date'],
'business_reason' => $attributes['business_reason'],
'category' => $attributes['category'],
'project_code' => $attributes['project_code'],
'is_foreign_expense' => $attributes['is_foreign_expense'],
'currency_code' => $attributes['currency_code'],
'conversion_rate_to_gbp' => $attributes['conversion_rate_to_gbp'],
'amount_before_conversion' => $attributes['amount_before_conversion'],
'gross_amount' => $attributes['gross_amount'],
'has_vat' => $attributes['has_vat'],
'vat_claimed' => $attributes['vat_claimed'],
]);
return $expense;
}
/**
* Remove an existing expense.
*
* @param \App\Models\Expense $expense
*
* @return bool
*/
public function deleteExpense(Expense $expense)
{
$expense->delete();
return $expense;
}
}
Then in my controller:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreExpense;
use App\Models\Expense;
use App\Models\ExpenseClaim;
use App\Services\ExpenseService;
class UserExpenseController extends Controller
{
protected $expenseService;
/**
* Create a new controller instance.
* Pass in our expense service class
*
* @param \App\Services\ExpenseService $expenseService
*/
public function __construct(ExpenseService $expenseService)
{
$this->expenseService = $expenseService;
}
/**
* Show a listing of expenses for a given user.
*
* @return void
*/
public function index(ExpenseClaim $expenseClaim)
{
$expenses = $this->expenseService->retrieveExpenses($expenseClaim);
return response()->json($expenses, 200);
}
/**
* Create a new expense inside an expense claim for a given user.
*
* @param ExpenseClaim $expenseClaim
* @return void
*/
public function store(ExpenseClaim $expenseClaim, StoreExpense $request)
{
$this->authorize('update', $expenseClaim);
$attributes = $request->validated();
$expense = $this->expenseService->createExpense($expenseClaim, auth()->user(), $attributes);
return response()->json($expense, 200);
}
/**
* Update an existing expense in an expense claim for a given user.
*
* @param \App\Models\ExpenseClaim $expenseClaim
* @param \App\Models\Expense $expense
*
* @return void
*/
public function update(ExpenseClaim $expenseClaim, Expense $expense, StoreExpense $request)
{
$this->authorize('update', $expenseClaim);
$attributes = $request->validated();
$expense = $this->expenseService->updateExpense($expense, auth()->user(), $attributes);
return response()->json($expense, 200);
}
/**
* Display a specific expense within an expense claim
*
* @param ExpenseClaim $expenseClaim
* @param Expense $expense
* @return void
*/
public function show(ExpenseClaim $expenseClaim, Expense $expense)
{
$this->authorize('update', $expenseClaim);
return response()->json($expense, 200);
}
/**
* Delete a given expense for a given user.
*
* @param ExpenseClaim $expenseClaim
* @return void
*/
public function destroy(ExpenseClaim $expenseClaim, Expense $expense)
{
$this->authorize('update', $expenseClaim);
$expense = $this->expenseService->deleteExpense($expense);
return response()->json($expense, 200);
}
}
My thought was that admin need to view submitted claims so a service class made sense, but how do you follow Single Responsibility if one thing can't exist without the other?
Thanks in advance.