Skip to content

Commit

Permalink
Merge pull request #39 from phi-rakib/29-implement-crud-operations-fo…
Browse files Browse the repository at this point in the history
…r-products

29 implement crud operations for products
  • Loading branch information
phi-rakib authored Jun 4, 2024
2 parents 1acc736 + f9cd511 commit 9777698
Show file tree
Hide file tree
Showing 21 changed files with 781 additions and 0 deletions.
75 changes: 75 additions & 0 deletions app/Http/Controllers/ProductController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreProductRequest;
use App\Models\Price;
use App\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;

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

return Product::latest()->with(['category', 'brand', 'unitType', 'warehouses', 'creator'])->paginate(20);
}

public function show(Product $product)
{
Gate::authorize('view', $product);

return $product->load(['category', 'brand', 'unitType', 'warehouses', 'creator', 'latestPrice', 'prices', 'attributes']);
}

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

DB::transaction(function () use ($request) {
$product = Product::create($request->validated());

$product->prices()->create(['price' => $request->price]);

$product->attributes()->attach($request->input('attributes'));
});

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

public function update(StoreProductRequest $request, Product $product)
{
Gate::authorize('update', $product);

DB::transaction(function () use ($request, $product) {
$product->update($request->validated());

$product->attributes()->sync($request->input('attributes'));

$latestPrice = $product->latestPrice;

if ($latestPrice == null || $latestPrice->price != $request->input('price')) {
Price::create([
'product_id' => $product->id,
'price' => $request->input('price'),
]);
}
});

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

public function destroy(Product $product)
{
Gate::authorize('delete', $product);

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

$product->delete();

return response()->json(['message' => 'Product deleted successfully'], 204);
}
}
35 changes: 35 additions & 0 deletions app/Http/Requests/StoreProductRequest.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 StoreProductRequest 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 [
'category_id' => 'required|integer|exists:categories,id',
'brand_id' => 'required|integer|exists:brands,id',
'unit_type_id' => 'required|integer|exists:unit_types,id',
'name' => 'required|max:255',
'description' => 'nullable',
'price' => 'required|numeric',
'attributes' => 'array',
];
}
}
5 changes: 5 additions & 0 deletions app/Models/Brand.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ public function deleter()
{
return $this->belongsTo(User::class, 'deleted_by')->select(['id', 'name']);
}

public function products()
{
return $this->hasMany(Product::class);
}
}
5 changes: 5 additions & 0 deletions app/Models/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ public function deleter()
{
return $this->belongsTo(User::class, 'deleted_by')->select(['id', 'name']);
}

public function products()
{
return $this->hasMany(Product::class);
}
}
21 changes: 21 additions & 0 deletions app/Models/Price.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Models;

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

class Price extends Model
{
use HasFactory;

protected $fillable = [
'price',
'product_id',
];

public function product()
{
return $this->belongsTo(Product::class);
}
}
71 changes: 71 additions & 0 deletions app/Models/Product.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace App\Models;

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

class Product extends Model
{
use HasFactory, SoftDeletes;

protected $fillable = [
'name',
'description',
'image',
'category_id',
'brand_id',
'unit_type_id',
];

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

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

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

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

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

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

public function warehouses()
{
return $this->belongsToMany(Warehouse::class)->withPivot(['quantity']);
}

public function prices()
{
return $this->hasMany(Price::class);
}

public function latestPrice()
{
return $this->hasOne(Price::class)->latestOfMany();
}

public function attributes()
{
return $this->belongsToMany(Attribute::class);
}
}
5 changes: 5 additions & 0 deletions app/Models/UnitType.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public function deleter()
{
return $this->belongsTo(User::class, 'deleted_by')->select(['id', 'name']);
}

public function products()
{
return $this->hasMany(Product::class);
}
}
5 changes: 5 additions & 0 deletions app/Models/Warehouse.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ public function deleter()
{
return $this->belongsTo(User::class, 'deleted_by')->select(['id', 'name']);
}

public function products()
{
return $this->belongsToMany(Product::class)->withPivot(['quantity']);
}
}
21 changes: 21 additions & 0 deletions app/Observers/ProductObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Observers;

use App\Models\Product;

class ProductObserver
{
public function creating(Product $product)
{
$product->slug = str($product->name)->slug()->toString();
$product->created_by = auth()->id();
$product->status = 'active';
}

public function updating(Product $product)
{
$product->slug = str($product->name)->slug()->toString();
$product->updated_by = auth()->id();
}
}
65 changes: 65 additions & 0 deletions app/Policies/ProductPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Policies;

use App\Models\Product;
use App\Models\User;

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

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

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

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

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

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

/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Product $product): bool
{
return $user->can('product-force-delete');
}
}
3 changes: 3 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\PaymentMethod;
use App\Models\Product;
use App\Models\UnitType;
use App\Models\Warehouse;
use App\Observers\AccountObserver;
Expand All @@ -24,6 +25,7 @@
use App\Observers\ExpenseCategoryObserver;
use App\Observers\ExpenseObserver;
use App\Observers\PaymentMethodObserver;
use App\Observers\ProductObserver;
use App\Observers\UnitTypeObserver;
use App\Observers\WarehouseObserver;
use Illuminate\Support\ServiceProvider;
Expand Down Expand Up @@ -54,6 +56,7 @@ public function boot(): void
UnitType::observe(UnitTypeObserver::class);
Attribute::observe(AttributeObserver::class);
AttributeValue::observe(AttributeValueObserver::class);
Product::observe(ProductObserver::class);
Warehouse::observe(WarehouseObserver::class);
}
}
23 changes: 23 additions & 0 deletions database/factories/PriceFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Price>
*/
class PriceFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'price' => fake()->numberBetween(100, 1000),
];
}
}
Loading

0 comments on commit 9777698

Please sign in to comment.