Laravel product attribute in single product admin panel
//Products Attributes
Route::match(['get','post'], 'add-attributes/{id}', 'addAttributes')->name('add.attributes');
//product atributes
public function addAttributes(Request $request, $id=null)
{
$products_details = Product::with('attributes')->where(['id'=>$id])->first();
if($request->isMethod('post')){
$data = $request->all();
// echo "<pre>"; print_r($data); die;
foreach($data['sku'] as $key => $val){
if(!empty($val)){
//prevent duplicate sku record
$attrCountSku = ProductAttribute::where('sku', $val)->count();
if($attrCountSku>0){
return redirect('/add-attributes/'.$id)->with('error', 'Sku is already Exists plz select another Sku.');
}
//prevent duplicate size record
$attrCountSize = ProductAttribute::where(['product_id' => $id, 'size'=>$data['size'][$key]])->count();
if($attrCountSize>0){
return redirect('/add-attributes/'.$id)->with('error', ''. $data['size'][$key]. 'Size is already exists plz select another size');
}
$attribute = new ProductAttribute();
$attribute->product_id = $id;
$attribute->sku = $val;
$attribute->size = $data['size'][$key];
$attribute->price = $data['price'][$key];
$attribute->stock = $data['stock'][$key];
$attribute->save();
}
}
return redirect('/add-attributes/'.$id)->with('success', 'Product Attribute Added successfully!');
}
return view('admins.product.add_attributes', compact('products_details'));
} //end method
//migration file
Schema::create('product_attributes', function (Blueprint $table) {
$table->id();
$table->integer('product_id');
$table->string('sku');
$table->string('size');
$table->double('price',8,2);
$table->integer('stock');
$table->timestamps();
});
//Relationship
//Product.php
public function attributes()
{
return $this->hasMany(ProductAttribute::class, 'product_id');
}
//productattribute.php
public function product()
{
return $this->belongsTo(Product::class);
}
//blade file code for insert attribute to db from admin panel
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3>Add Product Attributes</h3>
</div>
<div class="card-body">
<form action="{{route('add.attributes', $products_details->id)}}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="name"><b style="font-size:20px;">Product Title:</b></label>
<a target="_blank" href="{{url('/view-product/'.$products_details->slug)}}" style="font-size: 20px;">{{$products_details->title}}</a>
</div>
<div class="form-group">
<div class="field_wrapper">
<div style="display: flex;">
<input type="text" name="sku[]" id="sku" style="width:150px;" placeholder="SKU" class="form-control"/>
<input type="text" name="size[]" id="size" style="width:150px;" placeholder="Size" class="form-control "/>
<input type="text" name="price[]" id="price" style="width:150px;" placeholder="Price" class="form-control "/>
<input type="text" name="stock[]" id="stock" style="width:150px;" placeholder="Stock" class="form-control "/>
<a href="javascript:void(0);" class="add_button btn btn-primary " title="Add Field">Add</a>
</div>
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" style="width:15%;">Add Attributes</button>
</div>
</form>
</div>
</div>
</div>
</div>
//view attributes blade code
<div class="row">
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
@if (Session::has('success'))
<div class="alert alert-success">
{{ Session::get('success') }}
</div>
@endif
<p class="card-title mb-2">View Attributes</p>
<div class="table-responsive">
<table class="table table-striped table-bordered mt-3" id="userstab">
<thead>
<tr>
<th>SL</th>
<th>Product ID</th>
<th>SKU</th>
<th>Size</th>
<th>Price</th>
<th>Stock</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach ($products_details['attributes'] as $attribute)
<tr>
<td>{{$attribute->id}}</td>
<td>{{$attribute->product_id}}</td>
<td>{{$attribute->sku}}</td>
<td>{{$attribute->size}}</td>
<td>{{$attribute->price}}</td>
<td>{{$attribute->stock}}</td>
<td>
<a href="{{route('destroy.attributes', $attribute->id)}}"
class="btn p-0" id="delete" onclick="return confirm('Are you sure want to delete?')"><img src="{{ asset('delete.svg') }}" style="width: 27px !important;"></a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
//destroy attribute
Route::get('destroy-attributes/{id}', 'destroyAttributes')->name('destroy.attributes');
//controller code
public function destroyAttributes($id=null)
{
ProductAttribute::where(['id'=>$id])->delete();
return redirect()->back()->with('success', 'Product Attribute Deleted!');
} //end method
//product attributes display on front end
Route::get('/product/{id}/attributes', 'getProductAttributes');
//product attributes get on Front end
public function getProductAttributes($id)
{
$attributes = ProductAttribute::where('product_id', $id)->get();
return response()->json($attributes);
} //end method
//prduct details page
<p class="ezytor-paragraph-5 ezytor-fw-600 text-ash mt-3 mb-1 ">Price:
<span id="product-price" class="ezytor-heading-4 ezytor-fw-700 text-dark ms-3">${{ $product->selling_price }}</span>
</p>
<form id="update-quantity-form" action="{{ route('front.addtocart') }}" method="POST">
@csrf
<div class="size d-flex">
<p class="ezytor-paragraph-5 ezytor-fw-600 text-ash">Size:</p>
<ul class="ms-4 ezytor-paragraph-3">
@foreach($product->attributes as $attribute)
<li class="ms-1" style="margin-top:-14px; font-size:18px;">
<input type="radio" id="size_{{ $attribute->id }}" name="size" value="{{ $attribute->size }}" class="size-option" data-price="{{ $attribute->price }}">
<label for="size_{{ $attribute->id }}">{{ $attribute->size }}</label>
</li>
@endforeach
</ul>
</div>
<div class="d-flex mt-md-2 mt-4">
<div class="rounded-pill border-1 d-flex justify-content-around ezytor-paragraph-5 ezytor-fw-600">
<input type="hidden" value="{{ $product->id }}" name="product_id">
<button type="button" class="decrement-btn border-0 bg-transparent me-3 text-ashter m-0">-</button>
<input type="text" name="product_qty" id="product-quantity" value="1" class="form-control">
<button type="button" class="increment-btn border-0 bg-transparent ms-3 text-ashter m-0">+</button>
</div>
<div class="ms-3">
<input class="ezytor-paragraph-5 ezytor-fw-600 text-white border-0 bg-primary rounded-pill ezytor-py-14 ezytor-px-47 bg-primary" type="submit" value="Add to cart">
</div>
</div>
</form>
//here is scripts for get size attribute & update quantity with based on attribute price
{{-- Get product attributes --}}
<script>
$(document).ready(function(){
function updatePrice(size) {
var productId = $('input[name="product_id"]').val();
$.ajax({
url: '/product/' + productId + '/attributes',
type: 'GET',
success: function(response) {
var selectedAttribute = response.find(attribute => attribute.size === size);
$('#product-price').text('$' + selectedAttribute.price.toFixed(2));
}
});
}
// Initial price update for the default checked size (XL)
updatePrice('XL');
// Update price on size change
$('.size-option').on('change', function() {
var size = $(this).val();
updatePrice(size);
});
});
</script>
{{-- Product quantity update --}}
<script>
$(document).ready(function() {
// Initialize the selected attribute price
var selectedAttributePrice = 0;
// Attach event handler for attribute selection
$('.size-option').on('change', function() {
selectedAttributePrice = parseFloat($(this).data('price'));
updatePrice(); // Update price based on selected attribute and quantity
});
// Attach event handler for the increment button
$('.increment-btn').on('click', function(event) {
event.preventDefault(); // Prevent form submission or default behavior
var quantityInput = $('#product-quantity');
var currentQuantity = parseInt(quantityInput.val(), 10); // Ensure it's an integer
quantityInput.val(currentQuantity + 1); // Increment the quantity by 1
updatePrice(); // Call function to update the price based on new quantity
});
// Attach event handler for the decrement button
$('.decrement-btn').on('click', function(event) {
event.preventDefault();
var quantityInput = $('#product-quantity');
var currentQuantity = parseInt(quantityInput.val(), 10);
if (currentQuantity > 1) { // Ensure quantity doesn't go below 1
quantityInput.val(currentQuantity - 1); // Decrement by 1
updatePrice(); // Call function to update the price based on new quantity
}
});
// Function to update the product price
function updatePrice() {
var quantity = parseInt($('#product-quantity').val(), 10);
var newPrice = selectedAttributePrice * quantity;
$('#product-price').text('$' + newPrice.toFixed(2));
}
});
</script>
No comments