Initial commit

This commit is contained in:
2026-10-31 13:24:24 +01:00
commit e6805f2245
87 changed files with 16180 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
<?php
namespace App\Http\Controllers;
use App\Models\Material;
use App\Models\Product;
use Illuminate\Http\Request;
class CartController extends Controller
{
public function index()
{
$cart = session()->get('cart', []);
$subtotal = 0;
foreach ($cart as $item) {
$subtotal += $item['price'] * $item['quantity'];
}
$shipping = $subtotal > 50 || empty($cart) ? 0.00 : 4.95;
$total = $subtotal + $shipping;
return view('cart.index', compact('cart', 'subtotal', 'shipping', 'total'));
}
public function add(Request $request)
{
$validated = $request->validate([
'product_id' => 'required|exists:products,id',
'material' => 'required|string',
'color' => 'required|string',
'scale' => 'nullable|string',
'infill' => 'nullable|string',
'quantity' => 'required|integer|min:1|max:50',
]);
$product = Product::findOrFail($validated['product_id']);
$material = Material::where('code', $validated['material'])->first();
// Prijs berekening inclusief materiaal vermenigvuldiger & schaal
$unitPrice = $product->base_price;
if ($material) {
$unitPrice *= $material->price_multiplier;
}
// Schaal aanpassing
$scale = $validated['scale'] ?? '100%';
if ($scale === '150%') {
$unitPrice *= 1.45;
} elseif ($scale === '200%') {
$unitPrice *= 1.95;
}
$unitPrice = round($unitPrice, 2);
// Unieke sleutel in sessie op basis van product + configuratie
$cartKey = md5($product->id . '_' . $validated['material'] . '_' . $validated['color'] . '_' . $scale);
$cart = session()->get('cart', []);
if (isset($cart[$cartKey])) {
$cart[$cartKey]['quantity'] += (int)$validated['quantity'];
} else {
$cart[$cartKey] = [
'id' => $product->id,
'name' => $product->name,
'slug' => $product->slug,
'image_url' => $product->image_url,
'material' => $material ? $material->name : $validated['material'],
'material_code' => $validated['material'],
'color' => $validated['color'],
'scale' => $scale,
'infill' => $validated['infill'] ?? 'Standaard (20%)',
'price' => $unitPrice,
'quantity' => (int)$validated['quantity'],
];
}
session()->put('cart', $cart);
if ($request->wantsJson()) {
return response()->json([
'success' => true,
'message' => 'Artikel toegevoegd aan je winkelwagen!',
'cartCount' => array_sum(array_column($cart, 'quantity')),
]);
}
return redirect()->route('cart.index')->with('success', "{$product->name} is toegevoegd aan je winkelwagen!");
}
public function update(Request $request)
{
$validated = $request->validate([
'key' => 'required|string',
'quantity' => 'required|integer|min:1|max:50',
]);
$cart = session()->get('cart', []);
if (isset($cart[$validated['key']])) {
$cart[$validated['key']]['quantity'] = (int)$validated['quantity'];
session()->put('cart', $cart);
}
return redirect()->route('cart.index')->with('success', 'Winkelwagen bijgewerkt.');
}
public function remove($key)
{
$cart = session()->get('cart', []);
if (isset($cart[$key])) {
unset($cart[$key]);
session()->put('cart', $cart);
}
return redirect()->route('cart.index')->with('success', 'Artikel verwijderd uit je winkelwagen.');
}
public function count()
{
$cart = session()->get('cart', []);
$totalItems = array_sum(array_column($cart, 'quantity'));
return response()->json(['count' => $totalItems]);
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers;
use App\Models\Order;
use App\Models\OrderItem;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class CheckoutController extends Controller
{
public function index()
{
$cart = session()->get('cart', []);
if (empty($cart)) {
return redirect()->route('cart.index')->with('error', 'Je winkelwagen is nog leeg.');
}
$subtotal = 0;
foreach ($cart as $item) {
$subtotal += $item['price'] * $item['quantity'];
}
$shipping = $subtotal > 50 ? 0.00 : 4.95;
$total = $subtotal + $shipping;
return view('checkout.index', compact('cart', 'subtotal', 'shipping', 'total'));
}
public function store(Request $request)
{
$cart = session()->get('cart', []);
if (empty($cart)) {
return redirect()->route('cart.index')->with('error', 'Je winkelwagen is leeg.');
}
$validated = $request->validate([
'customer_name' => 'required|string|max:255',
'customer_email' => 'required|email|max:255',
'customer_phone' => 'nullable|string|max:50',
'shipping_street' => 'required|string|max:255',
'shipping_number' => 'required|string|max:50',
'shipping_postal_code' => 'required|string|max:20',
'shipping_city' => 'required|string|max:100',
'notes' => 'nullable|string|max:1000',
'payment_method' => 'required|string|in:ideal,bancontact,overboeking',
]);
$subtotal = 0;
foreach ($cart as $item) {
$subtotal += $item['price'] * $item['quantity'];
}
$shipping = $subtotal > 50 ? 0.00 : 4.95;
$total = $subtotal + $shipping;
$orderNumber = 'J3D-' . date('Y') . '-' . strtoupper(Str::random(6));
$order = Order::create([
'order_number' => $orderNumber,
'customer_name' => $validated['customer_name'],
'customer_email' => $validated['customer_email'],
'customer_phone' => $validated['customer_phone'] ?? null,
'shipping_street' => $validated['shipping_street'],
'shipping_number' => $validated['shipping_number'],
'shipping_postal_code' => $validated['shipping_postal_code'],
'shipping_city' => $validated['shipping_city'],
'shipping_country' => 'Nederland',
'notes' => $validated['notes'] ?? null,
'status' => 'in_behandeling',
'subtotal' => $subtotal,
'shipping_cost' => $shipping,
'total' => $total,
'payment_status' => 'in_afwachting',
'payment_method' => $validated['payment_method'],
]);
foreach ($cart as $item) {
OrderItem::create([
'order_id' => $order->id,
'product_id' => $item['id'],
'product_name' => $item['name'],
'material' => $item['material'] ?? null,
'color' => $item['color'] ?? null,
'scale' => $item['scale'] ?? '100%',
'infill' => $item['infill'] ?? 'Standaard (20%)',
'quantity' => $item['quantity'],
'unit_price' => $item['price'],
'total_price' => $item['price'] * $item['quantity'],
]);
}
// Leeg winkelwagen na succesvolle plaatsing
session()->forget('cart');
return redirect()->route('checkout.success', $order->order_number);
}
public function success($orderNumber)
{
$order = Order::with('items')->where('order_number', $orderNumber)->firstOrFail();
return view('checkout.success', compact('order'));
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
+136
View File
@@ -0,0 +1,136 @@
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Color;
use App\Models\CustomRequest;
use App\Models\Material;
use App\Models\Product;
use Illuminate\Http\Request;
class ShopController extends Controller
{
public function index()
{
$categories = Category::where('is_featured', true)
->orderBy('sort_order')
->take(6)
->get();
$featuredProducts = Product::with('category')
->where('is_active', true)
->where('is_featured', true)
->latest()
->take(6)
->get();
$materials = Material::where('is_active', true)->get();
return view('shop.index', compact('categories', 'featuredProducts', 'materials'));
}
public function catalog(Request $request)
{
$query = Product::with('category')->where('is_active', true);
// Filter op categorie
if ($request->filled('category')) {
$query->whereHas('category', function ($q) use ($request) {
$q->where('slug', $request->category);
});
}
// Filter op materiaal
if ($request->filled('material')) {
$query->whereJsonContains('available_materials', $request->material);
}
// Zoekfunctie
if ($request->filled('q')) {
$search = $request->q;
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('short_description', 'like', "%{$search}%")
->orWhere('description', 'like', "%{$search}%");
});
}
// Sortering
$sort = $request->get('sort', 'newest');
if ($sort === 'price_asc') {
$query->orderBy('base_price', 'asc');
} elseif ($sort === 'price_desc') {
$query->orderBy('base_price', 'desc');
} else {
$query->latest();
}
$products = $query->paginate(12)->withQueryString();
$categories = Category::orderBy('name')->get();
$materials = Material::where('is_active', true)->get();
return view('shop.catalog', compact('products', 'categories', 'materials'));
}
public function show($slug)
{
$product = Product::with('category')->where('slug', $slug)->firstOrFail();
$allMaterials = Material::where('is_active', true)->get()->keyBy('code');
$allColors = Color::where('in_stock', true)->get();
$relatedProducts = Product::where('id', '!=', $product->id)
->where('category_id', $product->category_id)
->where('is_active', true)
->take(3)
->get();
return view('shop.show', compact('product', 'allMaterials', 'allColors', 'relatedProducts'));
}
public function customRequest()
{
$materials = Material::where('is_active', true)->get();
$colors = Color::where('in_stock', true)->get();
return view('shop.custom', compact('materials', 'colors'));
}
public function storeCustomRequest(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|max:255',
'phone' => 'nullable|string|max:50',
'description' => 'required|string|max:2000',
'preferred_material' => 'nullable|string|max:50',
'preferred_color' => 'nullable|string|max:50',
'dimensions' => 'nullable|string|max:100',
'model_file' => 'nullable|file|max:51200', // max 50MB (stl, step, obj, zip)
]);
$filePath = null;
$originalName = null;
if ($request->hasFile('model_file')) {
$file = $request->file('model_file');
$originalName = $file->getClientOriginalName();
$filePath = $file->store('custom_models', 'public');
}
CustomRequest::create([
'name' => $validated['name'],
'email' => $validated['email'],
'phone' => $validated['phone'] ?? null,
'description' => $validated['description'],
'preferred_material' => $validated['preferred_material'] ?? null,
'preferred_color' => $validated['preferred_color'] ?? null,
'dimensions' => $validated['dimensions'] ?? null,
'file_path' => $filePath,
'original_filename' => $originalName,
'status' => 'nieuw',
]);
return redirect()->route('custom.request')->with('success', 'Bedankt! Jouw 3D print aanvraag is succesvol ontvangen. We beoordelen je model en sturen binnen 24 uur een vrijblijvende prijsopgave.');
}
}