I want to add data to the transaction and transaction_product tables in one form, but when I try the program code above there is an error message "The products must be an array." how should I deal with this?
Route
Route::post('/store-transaction', [App\Http\Controllers\TransactionController::class, 'store'])->name('transactions.store');
Controller:
public function store(Request $request) {
$products = json_decode($request->products, true);
// Validate the request
$request->validate([
'customer_name' => 'required|string|max:255',
'payment_id' => 'required|exists:payments,id',
'user_id' => 'required|exists:users,id',
'products' => 'required|array',
'products.*.id' => 'required|exists:products,id',
'products.*.quantity' => 'required|integer|min:1',
]);
// Generate a unique transaction code
$transactionCode = $this->generateUniqueTransactionCode();
// Calculate the total amount
$totalAmount = 0;
foreach ($products as $product) {
$productModel = Product::find($product['id']);
$totalAmount += $productModel->price * $product['quantity'];
}
// Create the transaction
$transaction = Transaction::create([
'transaction_code' => $transactionCode,
'transaction_date' => now(),
'customer_name' => $request->customer_name,
'total_amount' => $totalAmount,
'status' => 'Pending', // Default status
'payment_id' => $request->payment_id,
'user_id' => Auth::id()
]);
$products = $request->input('products');
// Add products to the transaction_products table
foreach ($products as $product) {
$productModel = Product::find($product['id']);
TransactionProduct::create([
'transaction_code_product' => $transactionCode,
'product_id' => $product['id'],
'quantity' => $product['quantity'],
'unit_price' => $productModel->price,
'total_price' => $productModel->price * $product['quantity'],
]);
}
return response()->json([
'status' => 200,
]);
}
HTML:
<section>
<div class="card shadow mt-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="text-primary">Manage Transactions</h3>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addTransactionModal"><i
class="ti ti-plus"></i>
Transaction</button>
</div>
<div class="card-body">
<table class="table table-hover table-striped" id="myTable">
<thead>
<tr>
<th class="col-md-5">Transaction Code</th>
<th class="col-md-5">Customer Name</th>
<th class="col-md-2">Action</th>
</tr>
</thead>
</table>
</div>
</div>
{{-- Add Modal --}}
<div class="modal fade" id="addTransactionModal" tabindex="-1" aria-labelledby="exampleModalLabel"
data-bs-backdrop="static" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add New Transaction</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="#" method="POST" id="add_transaction_form" enctype="multipart/form-data">
@csrf
<div class="modal-body p-4">
<div class="row">
{{-- <input type="text" name="products" id="products"> --}}
<input type="hidden" name="user_id" value="{{ Auth::user()->id }}">
<div class="mb-3">
<label for="customer_name" class="form-label">Customer Name <b
style="color:Tomato;">*</b></label>
<input type="text" class="form-control" name="customer_name" id="customer_name"
placeholder="Enter the customer name">
<span class="text-danger"></span>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center">
<div></div>
<button type="button" class="btn btn-outline-primary" onclick="openProductModal()">
<i class="ti ti-plus"></i>
Product</button>
</div>
</div>
<div class="mb-3">
<table class="table" id="product-list">
<thead>
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Price</th>
<th>Total Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<!-- Dynamically added rows will appear here -->
</tbody>
</table>
<div class="mt-3 d-none" id="total-amount-container">
<div class="d-flex justify-content-between align-items-center">
<div></div>
<p>Total Amount: <span id="total-price">0.00</span></p>
</div>
</div>
</div>
<div class="mb-3">
<label for="payment" class="form-label">Payment Method <b
style="color:Tomato;">*</b></label>
<select class="form-select" aria-label="Default select example" name="payment_id"
id="payment_id">
<option hidden disabled selected value>Select a payment method</option>
@foreach ($payments as $payment)
<option value="{{ $payment->id }}">
{{ $payment->name }}
</option>
@endforeach
</select>
<span class="text-danger"></span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Close</button>
<button type="submit" id="add_transaction_btn" class="btn btn-primary">Add
Transaction</button>
</div>
</form>
</div>
</div>
</div>
{{-- End Add Modal --}}
<!-- Add Modal Transaction_Product -->
<div class="modal fade" id="selectProductModal" tabindex="-1" aria-labelledby="selectProductLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="selectProductLabel">Select Product</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<table class="table table-striped">
<thead>
<tr>
<th>Product Name</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@forelse ($products as $product)
<tr>
<td>{{ $product->name }}</td>
<td>{{ $product->price }}</td>
<td>
<button type="button" class="btn btn-primary btn-sm select-product-btn"
data-id="{{ $product->id }}" data-name="{{ $product->name }}"
data-price="{{ $product->price }}">
<i class="ti ti-plus"></i>
</button>
</td>
</tr>
@empty
<tr>
<td colspan="3">
<center>No data available in table</center>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</section>
Script on blade:
$("#add_transaction_form").submit(function(e) {
e.preventDefault();
// Prepare form data
const fd = new FormData(this);
let products = [];
// Collect product information from the UI
$("#product-list tbody tr").each(function() {
let productId = $(this).data('id');
let quantity = $(this).find('.product-qty').val();
let unitPrice = $(this).find('.product-qty').data('price');
let totalPrice = $(this).find('.product-total-price').text().trim();
products.push({
id: productId,
quantity: parseInt(quantity),
unit_price: parseFloat(unitPrice),
total_price: parseFloat(totalPrice)
});
});
// Append products to FormData
fd.append('products', JSON.stringify(products)); // Send products as JSON string
$("#add_transaction_btn").text('Adding...');
$.ajax({
url: '{{ route('transactions.store') }}',
method: 'POST',
data: fd,
cache: false,
contentType: false,
processData: false,
dataType: 'JSON',
success: function(response) {
if (response.status == 200) {
Toast.fire({
icon: 'success',
title: 'Transaction added successfully.'
});
$('#myTable').DataTable().ajax.reload();
$("#add_transaction_form")[0].reset();
$("#addTransactionModal").modal('hide');
$('span.text-danger').text('');
}
$("#add_transaction_btn").text('Add Transaction');
},
error: function(xhr, status, error) {
var errors = xhr.responseJSON.errors;
$('span.text-danger').text('');
$.each(errors, function(key, value) {
$('#' + key).next('span.text-danger').text(value[0]);
});
Toast.fire({
icon: 'error',
title: 'Something went wrong!'
});
$("#add_transaction_btn").text('Add Transaction');
}
});
});