Skip to content

Commit

Permalink
Merge pull request #16 from phi-rakib/implement-crud-operations-for-e…
Browse files Browse the repository at this point in the history
…xpense

Implement crud operations for expense
  • Loading branch information
phi-rakib authored Jun 2, 2024
2 parents 37f95a9 + d89705d commit 595618d
Show file tree
Hide file tree
Showing 11 changed files with 467 additions and 0 deletions.
57 changes: 57 additions & 0 deletions app/Http/Controllers/ExpenseController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreExpenseRequest;
use App\Models\Expense;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class ExpenseController extends Controller
{
public function index()
{
Gate::authorize('viewAny', Expense::class);

return Expense::with(['account', 'expenseCategory', 'paymentMethod', 'creator'])->latest()->paginate(20);
}

public function show(Expense $expense)
{
Gate::authorize('view', $expense);

$expense->load(['account', 'expenseCategory', 'paymentMethod', 'creator']);

return $expense;
}

public function store(StoreExpenseRequest $request)
{
Gate::authorize('create', Expense::class);

Expense::create($request->validated());

return response()->json(['message' => 'Expense created successfully.'], 201);
}

public function update(Request $request, Expense $expense)
{
Gate::authorize('update', $expense);

$expense->update($request->all());

return response()->json(['message' => 'Expense updated successfully.'], 200);
}

public function destroy(Expense $expense)
{
Gate::authorize('delete', $expense);

$expense->deleted_by = auth()->id();
$expense->save();

$expense->delete(); // soft delete

return response()->json(['message' => 'Expense deleted successfully.'], 204);
}
}
35 changes: 35 additions & 0 deletions app/Http/Requests/StoreExpenseRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;

class StoreExpenseRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Auth::check();
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'expense_category_id' => 'required|integer|exists:expense_categories,id',
'account_id' => 'required|integer|exists:accounts,id',
'payment_method_id' => 'required|integer|exists:payment_methods,id',
'expense_date' => 'required|date',
'amount' => 'required|numeric',
'description' => 'nullable',
];
}
}
42 changes: 42 additions & 0 deletions app/Models/Expense.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Expense extends Model
{
use HasFactory, SoftDeletes;

protected $fillable = [
'name',
'expense_category_id',
'account_id',
'payment_method_id',
'amount',
'expense_date',
'description',
];

public function expenseCategory()
{
return $this->belongsTo(ExpenseCategory::class)->select(['id', 'name']);
}

public function account()
{
return $this->belongsTo(Account::class)->select(['id', 'name', 'balance']);
}

public function paymentMethod()
{
return $this->belongsTo(PaymentMethod::class)->select(['id', 'name']);
}

public function creator()
{
return $this->belongsTo(User::class, 'created_by')->select(['id', 'name']);
}
}
18 changes: 18 additions & 0 deletions app/Observers/ExpenseObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Observers;

use App\Models\Expense;

class ExpenseObserver
{
public function creating(Expense $expense)
{
$expense->created_by = auth()->id();
}

public function updating(Expense $expense)
{
$expense->updated_by = auth()->id();
}
}
65 changes: 65 additions & 0 deletions app/Policies/ExpensePolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Policies;

use App\Models\Expense;
use App\Models\User;

class ExpensePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('expense-list');
}

/**
* Determine whether the user can view the model.
*/
public function view(User $user, Expense $expense): bool
{
return $user->can('expense-list');
}

/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('expense-create');
}

/**
* Determine whether the user can update the model.
*/
public function update(User $user, Expense $expense): bool
{
return $user->can('expense-edit');
}

/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Expense $expense): bool
{
return $user->can('expense-delete');
}

/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Expense $expense): bool
{
return $user->can('expense-restore');
}

/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Expense $expense): bool
{
return $user->can('expense-force-delete');
}
}
3 changes: 3 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
use App\Models\Account;
use App\Models\Deposit;
use App\Models\DepositCategory;
use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\PaymentMethod;
use App\Observers\AccountObserver;
use App\Observers\DepositCategoryObserver;
use App\Observers\DepositObserver;
use App\Observers\ExpenseCategoryObserver;
use App\Observers\ExpenseObserver;
use App\Observers\PaymentMethodObserver;
use Illuminate\Support\ServiceProvider;

Expand All @@ -34,5 +36,6 @@ public function boot(): void
Deposit::observe(DepositObserver::class);
PaymentMethod::observe(PaymentMethodObserver::class);
ExpenseCategory::observe(ExpenseCategoryObserver::class);
Expense::observe(ExpenseObserver::class);
}
}
32 changes: 32 additions & 0 deletions database/factories/ExpenseFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Database\Factories;

use App\Models\Account;
use App\Models\ExpenseCategory;
use App\Models\PaymentMethod;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Expense>
*/
class ExpenseFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->sentence(3),
'expense_category_id' => ExpenseCategory::factory(),
'account_id' => Account::factory(),
'payment_method_id' => PaymentMethod::factory(),
'amount' => fake()->numberBetween(100, 1000),
'expense_date' => fake()->date(),
'description' => fake()->sentence(5),
];
}
}
45 changes: 45 additions & 0 deletions database/migrations/2024_06_02_122537_create_expenses_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('expenses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('expense_category_id');
$table->unsignedBigInteger('account_id');
$table->unsignedBigInteger('payment_method_id');
$table->decimal('amount', 8, 2);
$table->date('expense_date');
$table->text('description')->nullable();
$table->unsignedBigInteger('created_by');
$table->unsignedBigInteger('updated_by')->nullable();
$table->unsignedBigInteger('deleted_by')->nullable();
$table->softDeletes();
$table->timestamps();

$table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('payment_method_id')->references('id')->on('payment_methods')->onDelete('cascade');
$table->foreign('created_by')->references('id')->on('users')->onDelete('cascade');
$table->foreign('updated_by')->references('id')->on('users')->onDelete('cascade');
$table->foreign('deleted_by')->references('id')->on('users')->onDelete('cascade');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('expenses');
}
};
6 changes: 6 additions & 0 deletions database/seeders/PermissionSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ public function run(): void
'expense-category-delete',
'expense-category-restore',
'expense-category-force-delete',
'expense-list',
'expense-create',
'expense-edit',
'expense-delete',
'expense-restore',
'expense-force-delete',
];

foreach ($permissions as $permission) {
Expand Down
2 changes: 2 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Http\Controllers\DepositCategoryController;
use App\Http\Controllers\DepositController;
use App\Http\Controllers\ExpenseCategoryController;
use App\Http\Controllers\ExpenseController;
use App\Http\Controllers\PaymentMethodController;
use Illuminate\Support\Facades\Route;

Expand All @@ -16,4 +17,5 @@
Route::apiResource('deposits', DepositController::class);
Route::apiResource('paymentMethods', PaymentMethodController::class);
Route::apiResource('expenseCategories', ExpenseCategoryController::class);
Route::apiResource('expenses', ExpenseController::class);
});
Loading

0 comments on commit 595618d

Please sign in to comment.