This is the 16th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
I. Order details management
1.1 Creating an Order Controller
Run the commandphp artisan make:controller Web/OrderController
Create order controller:Write order management-related methods:
namespace App\Http\Controllers\Web;
use App\Http\Controllers\BaseController;
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class OrderController extends BaseController
{
/** * Order list */
public function index(Request $request) {
// Status query
$status = $request->query('status');// According to the title
$title = $request->query('title');
$order = Order::where('user_id', auth('api')->id())
->when($status.function ($query) use ($status) {
$query->where('status'.$status);
})
->when($status.function ($query) use ($title) {
$query->whereHas('goods'.function ($query) use($title) {
$query->where('title'.'like'."%{$title}%");
}); // Add a custom constraint to the association
})
->paginate(3);
return $this->response->paginator($order.new OrderTransformer());
}
/** * preview the order */
public function preview()
{
// Address data
// TODO temporarily simulates address data
$address= [['id'= >1.'name'= >'Joe'.'address'= >'xiamen'.'phone'= >'123123'],
['id'= >2.'name'= >'bill'.'address'= >'xiamen'.'phone'= >'123123'],
['id'= >3.'name'= >'Wang Wu'.'address'= >'xiamen'.'phone'= >'123123']];// Shopping cart data
$carts = Cart::where('user_id', auth('api')->id())
->where('is_checked'.1)
->with('goods:id,cove,title')
->get();
// Return data
return $this->response->array([
'address'= >$address.'carts'= >$carts,]); }/** * submit the order */
public function store(Request $request)
{
// Verify the address field
$request->validate([
'address_id'= >'required' // TODO addresses exist :addresses,id
], [
'address_id.required'= > ['Harvest address cannot be empty']]);// Process the inserted data
$user_id = auth('api')->id();
$order_no = date('YmdHis') . rand(100000.999999);
$amount = 0;
$cartsQuery = Cart::where('user_id'.$user_id)
->where('is_checked'.1);
$carts = $cartsQuery->with('goods:id,price')->get();
// The order details to be inserted
$insertData = [];
foreach ($carts as $key= >$cart) {
// If an item is out of stock, prompt the user to choose again
if ($cart->goods->stock < $cart->num) {
return $this->response->errorBadRequest($cart->goods->title.'Stock is insufficient, please select another item! ');
}
$insertData[] = [
'goods_id'= >$cart->goods_id,
'price'= >$cart->goods->price,
'num'= >$cart->num
];
$amount+ =$cart->goods->price * $cart->num;
}
try {
DB::beginTransaction(); // Start the transaction
// Generate the order
$order = Order::create([
'user_id'= >$user_id.'order_no'= >$order_no.'address_id'= >$request->input('address_id'),
'amount'= >$amount
]);
// Generate details of the order
$order->orderDetails()->createMany($insertData);
// Delete the cart data that has already been settled
$cartsQuery->delete();
// Subtract the inventory of goods
foreach($carts as $cart) {
Good::where('id'.$cart->goods_id)->decrement('stock'.$cart->num);
}
DB::commit(); // Commit only if there is no exception
return $this->response->created();
} catch (\Exception $e) {
DB::rollBack(); // Abnormal transaction data rollback occurred
throw $e; // Throw an exception}}// Order details
public function show(Order $order) {
return $this->response->item($order.new OrderTransformer());
}
/** * confirm receipt of goods */
public function confirm(Order $order) {
if ($order->status ! =3) {
return $this->response->errorBadRequest('Order status abnormal! ');
}
try {
DB::beginTransaction();
$order->status = 4;
$order->save();
$orderDetails = $order->orderDetails;
// Increase the sales of all items under the order
foreach($orderDetails as $orderDetail) {
// Update product sales
Good::where('id'.$orderDetail->goods_id)->increment('sales'.$orderDetail->num);
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
return $this->response->noContent(); }}Copy the code
1. We haven’t done address management API yet, so the address is simulated temporarily. 2.
Since we are inserting data into the order table, we also write a batch assignable field to the order table:Since data is also inserted into the order detail table, we also need to insert batchable fields into the order detail table:
We then write remote one-to-many to the order model so that we can skip the intermediate order detail table and get the item data directly from the order model:
/** ** order remote one-to-many, associated goods */
public function goods() {
return $this->hasManyThrough(
Good::class, // The final associated model
OrderDetails::class, // The intermediate model
'order_id'.// The foreign key associated with the intermediate model
'id'.// The foreign key of the final correlation model
'id'.// This model is associated with the intermediate model
'goods_id' // The intermediate table and the final model are associated with a key
);
}
Copy the code
Add item data to OrderTransformer. PHP:
/**
* 商品数据
*/
public function includeGoods(Order $order) {
return $this->collection($order->goods, new GoodTransformer());
}
Copy the code
1. The task scheduling order is outdated, unpaid and cancelled
Since placing an order will reduce inventory, in order to prevent some people from placing malicious orders and reducing inventory, we can do a task scheduling and make an order how long it will be invalid. Enter the command:crontab -e
, enter 1 and press Enter:Enter in the editor* * * * * /home/vagrant/code/shopProjectApi/artisan schedule:run >> /dev/null 2>&1
. The absolute path to your project. thencontroller + x
The inputy
Save and exit. Then, inApp\Console\Kernel
In the input:
// Check the order status regularly. If the order is not paid within 10 minutes, it will be invalid
// Real projects usually use long links, because it can be real-time long links, when the time is up, directly void, and task scheduling is not so accurate before the same time display
// Help us perform tasks at a certain time like backup data...
$schedule->call(function () {
// info('hellp');
$orders = Order::where('status'.1)
->where('created_at'.'<', date('Y-m-d H:i:s', time()-600))
->with('orderDetails.goods') // Get the associated item
->get();
// Loop the order, modify the order status, restore the inventory
try {
DB::beginTransaction(); // Open the door
foreach($orders as $order) {
$order->status = 5;
$order->save();
// Restore the inventory
foreach($order->orderDetails as $details) {
$details->goods->increment('stock'.$details->num);
}
}
Db::commit();
} catch (\Exception $e) {
DB::rollBack();
Log::error($e);
}
})->everyMinute();
Copy the code
When the order expires, the order status will be set to 5 and the item quantity will be rolled back:
1.2 Creating an Order Route
/** ** order */
// Order preview page
$api->get('order/preview', [OrderController::class, 'preview']);
// Submit the order
$api->post('order', [OrderController::class, 'store']);
// Order details
$api->get('order/{order}', [OrderController::class, 'show']);
// Order list
$api->get('orders', [OrderController::class, 'index']);
// Confirm receipt of goods
$api->patch('orders/{order}/confirm', [OrderController::class, 'confirm']);
Copy the code
1.3 Test Effect
During the test, we found that the error was reported, because the order number was set as the order sheet when we were building itinteger
So let’s change the type of this field. Run the command firstcomposer require doctrine/dbal
.Then run:php artisan make:migration modify_order_no_column_in_orders_table --table=orders
Create a migration file that modifies this field:Then write:$table - > string (" order_no ") - > comment (' order number) - > change ();
The good type has been modified.
Next test:
You can see that the order is generated and the data in the shopping cart is gone.
If you find this article helpful on your way to learning PHP, please follow me to like and comment on it. Thank you, your blog is definitely another support for me to write.