Commit 6f3f1b5a authored by Le Dinh Trung's avatar Le Dinh Trung

Merge branch 'feature/order' into 'dev'

Feature/order

See merge request !10
parents eb2f5644 2057d743
......@@ -50,3 +50,5 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
JWT_SECRET=
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Resources\OrderResource;
use App\Repositories\OrderRepository;
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\Models\User;
class OrderController extends Controller
{
private OrderRepository $orderRepository;
private UserRepository $userRepository;
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function __construct(OrderRepository $orderRepository, UserRepository $userRepository)
{
$this->orderRepository = $orderRepository;
$this->userRepository = $userRepository;
}
public function index(Request $request)
{
$id = $request->id;
$user = auth('api')->user()->id;
if ($user->role == User::ROLE_ADMIN) {
$order = $this->orderRepository->getListOrderOfAdmin($request);
} elseif ($user->role == User::ROLE_EDITOR) {
$order = $this->orderRepository->getListOrderOfUser($id);
}
return response()->json([
'success' => true,
'meta' => [
'total' => $order->total(),
'pages' => $order->lastPage()
],
'data' => OrderResource::collection($order)
]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create(Request $request)
{
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$order = $this->orderRepository->create($request->only(['id', 'details', 'client', 'is_fulfilled']));
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show(Request $request)
{
$order = $this->orderRepository->getById($request->id);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(Request $request)
{
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$order = $this->orderRepository->update(
$request->id,
$request->only(['id', 'details', 'client', 'is_fulfilled'])
);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request)
{
$order = $this->orderRepository->deleteById($request->id);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use Validator;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'register']]);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required|string|min:6',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
if (!$token = auth('api')->attempt($validator->validated())) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->createNewToken($token);
}
/**
* Register a User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|between:2,100',
'email' => 'required|string|email|max:100|unique:users',
'password' => 'required|string|confirmed|min:6',
]);
if ($validator->fails()) {
return response()->json($validator->errors()->toJson(), 400);
}
$user = User::create(array_merge(
$validator->validated(),
['password' => bcrypt($request->password)]
));
return response()->json([
'message' => 'User successfully registered',
'user' => $user
], 201);
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth('api')->logout();
return response()->json(['message' => 'User successfully signed out']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->createNewToken(auth('api')->refresh());
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function userProfile()
{
return response()->json(auth('api')->user());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function createNewToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60,
'user' => auth('api')->user()
]);
}
public function changePassWord(Request $request)
{
$validator = Validator::make($request->all(), [
'old_password' => 'required|string|min:6',
'new_password' => 'required|string|confirmed|min:6',
]);
if ($validator->fails()) {
return response()->json($validator->errors()->toJson(), 400);
}
$userId = auth('api')->user()->id;
$user = User::where('id', $userId)->update(
['password' => bcrypt($request->new_password)]
);
return response()->json([
'message' => 'User successfully changed password',
'user' => $user,
], 201);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Resources\OrderResource;
use App\Repositories\OrderRepository;
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\Models\User;
class OrderController extends Controller
{
private OrderRepository $orderRepository;
private UserRepository $userRepository;
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function __construct(OrderRepository $orderRepository, UserRepository $userRepository)
{
$this->orderRepository = $orderRepository;
$this->userRepository = $userRepository;
}
public function index(Request $request)
{
$id = $request->id;
$user = $this->userRepository->getByid($id);
if ($user->role == User::ROLE_ADMIN) {
$order = $this->orderRepository->paginate($request->page);
} elseif ($user->role == User::ROLE_EDITOR) {
$order = $this->orderRepository->getListOrderOfUser($id);
}
return response()->json([
'success' => true,
'meta' => [
'total' => $order->total(),
'pages' => $order->lastPage()
],
'data' => OrderResource::collection($order)
]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create(Request $request)
{
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$order = $this->orderRepository->create($request->only(['id', 'details', 'client', 'is_fulfilled']));
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show(Request $request)
{
$order = $this->orderRepository->getById($request->id);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(Request $request)
{
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$order = $this->orderRepository->update(
$request->id,
$request->only(['id', 'details', 'client', 'is_fulfilled'])
);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request)
{
$order = $this->orderRepository->deleteById($request->id);
return response()->json([
'success' => true,
'message' => '',
'data' => $order
]);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class OrderDetailController extends Controller
{
//
}
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'details' => $this->details,
'client' => $this->client,
'user' => new UserResource($this->user),
'total' => $this->total,
'status' => $this->status_text,
'is_fulfilled' => $this->is_fulfilled,
'created_at' => $this->created_at
];
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
const UN_FULFILLED = false;
const FULFILLED = true;
const STATUS_PENDING = 0;
const STATUS_CONFIRMED = 1;
const STATUS_COMPLETED = 2;
const STATUS_CANCELED = 3;
protected $guarded = [];
protected $casts = [
'is_fulfilled' => 'boolean',
'total' => 'integer',
];
protected $attributes = [
'is_fulfilled' => Order::UN_FULFILLED,
];
public function getStatusTextAttribute($value)
{
if ($value == Order::STATUS_PENDING) {
return 'pending';
}
if ($value == Order::STATUS_CONFIRMED) {
return 'confirmed';
}
if ($value == Order::STATUS_COMPLETED) {
return 'completed';
}
if ($value == Order::STATUS_CANCELED) {
return 'canceled';
}
return $value;
}
public function details()
{
return $this->hasMany(OrderDetail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class OrderDetail extends Model
{
use HasFactory;
protected $guarded = [];
public function order()
{
return $this->belongsTo(Order::class);
}
public function getPayMoneyAttribute()
{
return $this->unit_price * $this->quantity * (100 - $this->discount) / 100;
}
}
......@@ -18,10 +18,12 @@ class Post extends Model
protected $attributes = [
'status' => true,
];
public function user() {
public function user()
{
return $this->belongsTo(User::class);
}
public function categories() {
public function categories()
{
return $this->belongsToMany(Category::class);
}
public function tags()
......
......@@ -2,13 +2,14 @@
namespace App\Models;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
class User extends Authenticatable implements JWTSubject
{
use HasApiTokens, HasFactory, Notifiable;
const STATUS_ACTIVE = 1;
......@@ -40,11 +41,34 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
];
public function posts() {
public function posts()
{
return $this->hasMany(Post::class);
}
}
public function comments()
{
return $this->hasMany(Comment::class);
}
// Rest omitted for brevity
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
<?php
namespace App\Repositories;
abstract class BaseRepository
{
protected $model;
public function getAll()
{
return $this->model->latest()->all();
}
public function getById($id)
{
return $this->model->findOrFail($id);
}
public function deleteById($id)
{
return $this->getById($id)->delete();
}
public function create(array $details)
{
return $this->model->create($details);
}
public function update($id, array $newDetails)
{
return $this->getById($id)->update($newDetails);
}
public function paginate($size = 15)
{
return $this->model->latest()->paginate($size);
}
}
<?php
namespace App\Repositories;
use App\Models\Order;
use App\Repositories\BaseRepository;
use Illuminate\Http\Request;
class OrderRepository extends BaseRepository
{
protected $model;
public function __construct(Order $model)
{
$this->model = $model;
}
public function getAll()
{
return $this->model->latest()->all();
}
public function getFulfilledOrder()
{
$this->model->where('is_fulfilled', true);
}
public function getListOrderOfAdmin(Request $request)
{
return $this->model->latest()->where('status', $request->status)->paginate();
}
public function getListOrderOfUser($id)
{
return $this->model->where('user_id', $id)->with('user')->paginate();
}
}
<?php
namespace App\Repositories;
use App\Models\User;
use App\Repositories\BaseRepository;
class UserRepository extends BaseRepository
{
protected $model;
public function __construct(User $model)
{
$this->model = $model;
}
public function getFulfilledOrder()
{
$this->model->where('is_fulfilled', true);
}
}
......@@ -36,6 +36,10 @@
*/
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'web' => [
'driver' => 'session',
'provider' => 'users',
......
This diff is collapsed.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class OrderDetailFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
//
];
}
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class OrderFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'user_id' => rand(1, 10),
'details' => $this->faker->sentences(4, true),
'client' => $this->faker->name(),
'is_fulfilled' => $this->faker->boolean(),
];
}
}
<?php
use App\Models\Order;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOrdersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->text('details')->nullable();
$table->string('client')->nullable();
$table->unsignedInteger('user_id');
$table->unsignedInteger('total')->default(0);
$table->unsignedSmallInteger('status')->default(Order::STATUS_PENDING);
$table->boolean('is_fulfilled')->default(Order::UN_FULFILLED);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('orders');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOrderDetailsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_details', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('unit_price')->default(0);
$table->unsignedInteger('quantity')->default(0);
$table->unsignedInteger('discount')->default(0);
$table->unsignedInteger('order_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('order_details');
}
}
......@@ -6,6 +6,8 @@
use App\Models\Category;
use App\Models\Post;
use Illuminate\Database\Seeder;
use App\Models\Order;
use App\Models\OrderDetail;
class DatabaseSeeder extends Seeder
{
......@@ -19,6 +21,7 @@ public function run()
User::factory(1)->create([
'role' => User::ROLE_ADMIN
]);
Order::factory(10)->has(OrderDetail::factory()->count(3), 'details')->create();
User::factory(9)->has(Post::factory()->hasCategories(1)->count(30), 'posts')->create();
Category::factory(10)->create();
}
......
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class OrderDetailSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Order;
class OrderSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Order::factory()->times(50)->create();
}
}
......@@ -2,7 +2,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;
/*
|--------------------------------------------------------------------------
| API Routes
......@@ -17,3 +17,23 @@
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('login', [App\Http\Controllers\AuthController::class, 'login']);
Route::post('logout', [App\Http\Controllers\AuthController::class, 'logout']);
Route::post('refresh', [App\Http\Controllers\AuthController::class, 'refresh']);
Route::get('user-profile', [App\Http\Controllers\AuthController::class, 'userProfile']);
Route::post('change-password', [App\Http\Controllers\AuthController::class, 'changePassword']);
});
Route::group([
'middleware' => 'api',
], function ($router) {
Route::resource('orders', App\Http\Controllers\Api\OrderController::class);
});
......@@ -5,6 +5,7 @@
use App\Http\Controllers\UserController;
use App\Http\Controllers\PostController;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\OrderController;
/*
|--------------------------------------------------------------------------
......@@ -25,4 +26,6 @@
});
Route::get('/', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::resource('orders', OrderController::class);
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment