New Initial Commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CartController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$cart = session()->get('cart', []);
|
||||
$total = 0;
|
||||
$items = [];
|
||||
|
||||
foreach ($cart as $itemKey => $cartItem) {
|
||||
// Handle both old and new cart formats
|
||||
if (is_array($cartItem)) {
|
||||
$productId = $cartItem['product_id'] ?? null;
|
||||
$type = $cartItem['type'] ?? 'wallpaper';
|
||||
$printStockId = $cartItem['print_stock_id'] ?? null;
|
||||
} else {
|
||||
// Old format: just the product ID
|
||||
$productId = $itemKey;
|
||||
$type = 'wallpaper';
|
||||
$printStockId = null;
|
||||
}
|
||||
|
||||
if ($productId) {
|
||||
$product = Product::find($productId);
|
||||
if ($product) {
|
||||
$quantity = is_array($cartItem) ? ($cartItem['quantity'] ?? 1) : $cartItem;
|
||||
$stockCost = 0;
|
||||
$stock = null;
|
||||
|
||||
// Get print stock if available
|
||||
if ($printStockId) {
|
||||
$stock = $product->printStocks()->find($printStockId);
|
||||
if ($stock) {
|
||||
$stockCost = ($type === 'wallpaper') ? $stock->cost_per_meter : $stock->cost_per_m2;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate price based on stock cost only (no base design cost)
|
||||
$basePrice = $stockCost;
|
||||
|
||||
if ($type === 'wallpaper') {
|
||||
$length = is_array($cartItem) ? ($cartItem['length'] ?? 0) : 0;
|
||||
$itemTotal = $basePrice * $length * $quantity;
|
||||
} elseif ($type === 'mural') {
|
||||
$width = is_array($cartItem) ? ($cartItem['width'] ?? 0) : 0;
|
||||
$height = is_array($cartItem) ? ($cartItem['height'] ?? 0) : 0;
|
||||
$m2 = $width * $height;
|
||||
$itemTotal = $basePrice * $m2 * $quantity;
|
||||
} else {
|
||||
$itemTotal = $basePrice * $quantity;
|
||||
}
|
||||
|
||||
$total += $itemTotal;
|
||||
$items[] = [
|
||||
'key' => $itemKey,
|
||||
'product' => $product,
|
||||
'stock' => $stock,
|
||||
'quantity' => $quantity,
|
||||
'type' => $type,
|
||||
'length' => is_array($cartItem) ? ($cartItem['length'] ?? null) : null,
|
||||
'width' => is_array($cartItem) ? ($cartItem['width'] ?? null) : null,
|
||||
'height' => is_array($cartItem) ? ($cartItem['height'] ?? null) : null,
|
||||
'subtotal' => $itemTotal
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view('cart', [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'itemCount' => count($cart)
|
||||
]);
|
||||
}
|
||||
|
||||
public function add(Request $request, Product $product)
|
||||
{
|
||||
$request->validate([
|
||||
'print_stock_id' => 'required|exists:print_stocks,id',
|
||||
'length' => $product->type === 'wallpaper' ? 'required|numeric|min:0.5' : 'nullable',
|
||||
'width' => $product->type === 'mural' ? 'required|numeric|min:0.5' : 'nullable',
|
||||
'height' => $product->type === 'mural' ? 'required|numeric|min:0.5' : 'nullable',
|
||||
]);
|
||||
|
||||
$cart = session()->get('cart', []);
|
||||
$cartItemKey = $product->id . '_' . $request->input('print_stock_id') . '_' . uniqid();
|
||||
|
||||
// Create a unique cart item with dimensions and stock
|
||||
$cartItem = [
|
||||
'product_id' => $product->id,
|
||||
'print_stock_id' => $request->input('print_stock_id'),
|
||||
'quantity' => 1,
|
||||
'type' => $product->type,
|
||||
];
|
||||
|
||||
if ($product->type === 'wallpaper') {
|
||||
$cartItem['length'] = $request->input('length');
|
||||
} elseif ($product->type === 'mural') {
|
||||
$cartItem['width'] = $request->input('width');
|
||||
$cartItem['height'] = $request->input('height');
|
||||
}
|
||||
|
||||
$cart[$cartItemKey] = $cartItem;
|
||||
session()->put('cart', $cart);
|
||||
|
||||
return redirect()->back()->with('success', $product->name . ' added to cart!');
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'item_key' => 'required',
|
||||
'quantity' => 'required|integer|min:1'
|
||||
]);
|
||||
|
||||
$cart = session()->get('cart', []);
|
||||
$itemKey = $request->input('item_key');
|
||||
$quantity = $request->input('quantity');
|
||||
|
||||
if (isset($cart[$itemKey])) {
|
||||
$cart[$itemKey]['quantity'] = $quantity;
|
||||
}
|
||||
|
||||
session()->put('cart', $cart);
|
||||
|
||||
return redirect()->back()->with('success', 'Cart updated!');
|
||||
}
|
||||
|
||||
public function remove(Request $request)
|
||||
{
|
||||
$itemKey = $request->input('item_key');
|
||||
$cart = session()->get('cart', []);
|
||||
|
||||
if (isset($cart[$itemKey])) {
|
||||
unset($cart[$itemKey]);
|
||||
}
|
||||
|
||||
session()->put('cart', $cart);
|
||||
|
||||
return redirect()->back()->with('success', 'Product removed from cart!');
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
session()->forget('cart');
|
||||
return redirect()->back()->with('success', 'Cart cleared!');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Product;
|
||||
|
||||
class FabricsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$fabrics = Product::take(6)->get();
|
||||
|
||||
return view('fabrics', [
|
||||
'fabrics' => $fabrics
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
use App\Models\HeroImage;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$categories = Category::all();
|
||||
$featuredWallpapers = Product::where('type', 'wallpaper')->where('featured', true)->take(3)->get();
|
||||
$featuredMurals = Product::where('type', 'mural')->where('featured', true)->take(3)->get();
|
||||
$heroImages = HeroImage::where('page', 'home')->where('is_active', true)->orderBy('sort_order')->get();
|
||||
|
||||
return view('home', [
|
||||
'categories' => $categories,
|
||||
'featuredWallpapers' => $featuredWallpapers,
|
||||
'featuredMurals' => $featuredMurals,
|
||||
'heroImages' => $heroImages
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
use App\Models\HeroImage;
|
||||
|
||||
class MuralsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$products = Product::where('type', 'mural')->get();
|
||||
$categories = Category::all();
|
||||
$heroImages = HeroImage::where('page', 'mural')->where('is_active', true)->orderBy('sort_order')->get();
|
||||
|
||||
return view('murals', [
|
||||
'products' => $products,
|
||||
'categories' => $categories,
|
||||
'heroImages' => $heroImages
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Product;
|
||||
use App\Models\PrintStock;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function checkout()
|
||||
{
|
||||
$cart = session()->get('cart', []);
|
||||
|
||||
if (empty($cart)) {
|
||||
return redirect()->route('cart')->with('error', 'Your cart is empty!');
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$total = 0;
|
||||
|
||||
foreach ($cart as $itemKey => $cartItem) {
|
||||
// Handle both old and new cart formats
|
||||
if (is_array($cartItem)) {
|
||||
$productId = $cartItem['product_id'] ?? null;
|
||||
$type = $cartItem['type'] ?? 'wallpaper';
|
||||
$printStockId = $cartItem['print_stock_id'] ?? null;
|
||||
} else {
|
||||
$productId = $itemKey;
|
||||
$type = 'wallpaper';
|
||||
$printStockId = null;
|
||||
}
|
||||
|
||||
if ($productId) {
|
||||
$product = Product::find($productId);
|
||||
if ($product) {
|
||||
$quantity = is_array($cartItem) ? ($cartItem['quantity'] ?? 1) : $cartItem;
|
||||
$stockCost = 0;
|
||||
$stock = null;
|
||||
|
||||
// Get print stock if available
|
||||
if ($printStockId) {
|
||||
$stock = $product->printStocks()->find($printStockId);
|
||||
if ($stock) {
|
||||
$stockCost = ($type === 'wallpaper') ? $stock->cost_per_meter : $stock->cost_per_m2;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate price based on stock cost only (no base design cost)
|
||||
$basePrice = $stockCost;
|
||||
|
||||
if ($type === 'wallpaper') {
|
||||
$length = is_array($cartItem) ? ($cartItem['length'] ?? 0) : 0;
|
||||
$subtotal = $basePrice * $length * $quantity;
|
||||
} elseif ($type === 'mural') {
|
||||
$width = is_array($cartItem) ? ($cartItem['width'] ?? 0) : 0;
|
||||
$height = is_array($cartItem) ? ($cartItem['height'] ?? 0) : 0;
|
||||
$m2 = $width * $height;
|
||||
$subtotal = $basePrice * $m2 * $quantity;
|
||||
} else {
|
||||
$subtotal = $basePrice * $quantity;
|
||||
}
|
||||
|
||||
$total += $subtotal;
|
||||
$items[] = [
|
||||
'key' => $itemKey,
|
||||
'product' => $product,
|
||||
'stock' => $stock,
|
||||
'quantity' => $quantity,
|
||||
'type' => $type,
|
||||
'length' => is_array($cartItem) ? ($cartItem['length'] ?? null) : null,
|
||||
'width' => is_array($cartItem) ? ($cartItem['width'] ?? null) : null,
|
||||
'height' => is_array($cartItem) ? ($cartItem['height'] ?? null) : null,
|
||||
'subtotal' => $subtotal
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view('checkout', [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'itemCount' => count($cart)
|
||||
]);
|
||||
}
|
||||
|
||||
public function process(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'customer_name' => 'required|string|max:255',
|
||||
'customer_email' => 'required|email',
|
||||
'customer_phone' => 'required|string|max:20',
|
||||
'shipping_address' => 'required|string|max:500',
|
||||
'notes' => 'nullable|string|max:500'
|
||||
]);
|
||||
|
||||
$cart = session()->get('cart', []);
|
||||
|
||||
if (empty($cart)) {
|
||||
return redirect()->route('cart')->with('error', 'Your cart is empty!');
|
||||
}
|
||||
|
||||
// Calculate total and validate stock
|
||||
$total = 0;
|
||||
$orderItems = [];
|
||||
|
||||
foreach ($cart as $itemKey => $cartItem) {
|
||||
// Handle both old and new cart formats
|
||||
if (is_array($cartItem)) {
|
||||
$productId = $cartItem['product_id'] ?? null;
|
||||
$quantity = $cartItem['quantity'] ?? 1;
|
||||
$type = $cartItem['type'] ?? 'wallpaper';
|
||||
$printStockId = $cartItem['print_stock_id'] ?? null;
|
||||
} else {
|
||||
$productId = $itemKey;
|
||||
$quantity = $cartItem;
|
||||
$type = 'wallpaper';
|
||||
$printStockId = null;
|
||||
}
|
||||
|
||||
$product = Product::find($productId);
|
||||
if (!$product) {
|
||||
return redirect()->route('cart')->with('error', 'Product not found!');
|
||||
}
|
||||
|
||||
if ($product->stock < $quantity) {
|
||||
return redirect()->route('cart')->with('error', "Insufficient stock for {$product->name}");
|
||||
}
|
||||
|
||||
$stockCost = 0;
|
||||
$stock = null;
|
||||
|
||||
// Get print stock if available
|
||||
if ($printStockId) {
|
||||
$stock = $product->printStocks()->find($printStockId);
|
||||
if ($stock) {
|
||||
$stockCost = ($type === 'wallpaper') ? $stock->cost_per_meter : $stock->cost_per_m2;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate price based on stock cost only (no base design cost)
|
||||
$basePrice = $stockCost;
|
||||
|
||||
if ($type === 'wallpaper') {
|
||||
$length = is_array($cartItem) ? ($cartItem['length'] ?? 0) : 0;
|
||||
$subtotal = $basePrice * $length * $quantity;
|
||||
} elseif ($type === 'mural') {
|
||||
$width = is_array($cartItem) ? ($cartItem['width'] ?? 0) : 0;
|
||||
$height = is_array($cartItem) ? ($cartItem['height'] ?? 0) : 0;
|
||||
$m2 = $width * $height;
|
||||
$subtotal = $basePrice * $m2 * $quantity;
|
||||
} else {
|
||||
$subtotal = $basePrice * $quantity;
|
||||
}
|
||||
|
||||
$total += $subtotal;
|
||||
$orderItems[$itemKey] = [
|
||||
'product_id' => $productId,
|
||||
'quantity' => $quantity,
|
||||
'price' => $product->price,
|
||||
'stock_cost' => $stockCost,
|
||||
'print_stock_id' => $printStockId,
|
||||
'type' => $type,
|
||||
'length' => is_array($cartItem) ? ($cartItem['length'] ?? null) : null,
|
||||
'width' => is_array($cartItem) ? ($cartItem['width'] ?? null) : null,
|
||||
'height' => is_array($cartItem) ? ($cartItem['height'] ?? null) : null,
|
||||
'subtotal' => $subtotal
|
||||
];
|
||||
}
|
||||
|
||||
// Create order
|
||||
$orderNumber = 'ORD-' . date('Ymd') . '-' . strtoupper(uniqid());
|
||||
|
||||
$orderData = [
|
||||
'user_id' => auth()->check() ? auth()->id() : null,
|
||||
'order_number' => $orderNumber,
|
||||
'total' => $total,
|
||||
'status' => 'pending',
|
||||
'payment_method' => 'yoco',
|
||||
'payment_status' => 'pending',
|
||||
'customer_name' => $request->input('customer_name'),
|
||||
'customer_email' => $request->input('customer_email'),
|
||||
'customer_phone' => $request->input('customer_phone'),
|
||||
'shipping_address' => $request->input('shipping_address'),
|
||||
];
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$orderData['notes'] = $request->input('notes');
|
||||
}
|
||||
|
||||
$order = Order::create($orderData);
|
||||
|
||||
// Create order items
|
||||
foreach ($orderItems as $itemKey => $data) {
|
||||
$product = Product::find($data['product_id']);
|
||||
|
||||
OrderItem::create([
|
||||
'order_id' => $order->uuid,
|
||||
'product_id' => $data['product_id'],
|
||||
'quantity' => $data['quantity'],
|
||||
'price' => $data['price'],
|
||||
'print_stock_id' => $data['print_stock_id'],
|
||||
'type' => $data['type'],
|
||||
'length' => $data['length'],
|
||||
'width' => $data['width'],
|
||||
'height' => $data['height']
|
||||
]);
|
||||
}
|
||||
|
||||
// Store order UUID in session for payment
|
||||
session(['pending_order_uuid' => $order->uuid]);
|
||||
|
||||
// Redirect to Yoco payment
|
||||
return redirect()->route('yoco-payment', ['order' => $order->uuid]);
|
||||
}
|
||||
|
||||
public function success(Order $order)
|
||||
{
|
||||
// Authorization: only allow viewing own orders or admin
|
||||
if (auth()->check() && auth()->user()->id !== $order->user_id && !auth()->user()->is_admin) {
|
||||
abort(403, 'Unauthorized access to this order.');
|
||||
}
|
||||
|
||||
// For guest orders, verify via session
|
||||
if (!auth()->check() && session('pending_order_uuid') !== $order->uuid) {
|
||||
abort(403, 'Unauthorized access to this order.');
|
||||
}
|
||||
|
||||
return view('order-success', ['order' => $order]);
|
||||
}
|
||||
|
||||
public function history()
|
||||
{
|
||||
$orders = Order::orderBy('created_at', 'desc')->get();
|
||||
|
||||
return view('order-history', ['orders' => $orders]);
|
||||
}
|
||||
|
||||
public function yocoPayment(Order $order)
|
||||
{
|
||||
// Verify this is a pending payment
|
||||
if ($order->payment_status !== 'pending') {
|
||||
abort(400, 'This order has already been paid.');
|
||||
}
|
||||
|
||||
// Create Yoco checkout
|
||||
$secretKey = config('services.yoco.secret_key');
|
||||
$mode = config('services.yoco.mode');
|
||||
|
||||
// Check if API key is configured
|
||||
if (empty($secretKey) || $secretKey === 'sk_test_your_key_here') {
|
||||
return redirect()->route('checkout')->with('error', 'Payment gateway not configured. Please contact support.');
|
||||
}
|
||||
|
||||
$baseUrl = $mode === 'live'
|
||||
? 'https://payments.yoco.com/api/checkouts'
|
||||
: 'https://payments.yoco.com/api/checkouts';
|
||||
|
||||
$checkoutData = [
|
||||
'amount' => (int)($order->total * 100), // Amount in cents
|
||||
'currency' => 'ZAR',
|
||||
'successUrl' => route('yoco-success', ['order' => $order->uuid]),
|
||||
'cancelUrl' => route('yoco-cancel', ['order' => $order->uuid]),
|
||||
'failureUrl' => route('yoco-failure', ['order' => $order->uuid]),
|
||||
'metadata' => [
|
||||
'order_uuid' => $order->uuid,
|
||||
'order_number' => $order->order_number,
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $secretKey,
|
||||
'Content-Type' => 'application/json',
|
||||
])->post($baseUrl, $checkoutData);
|
||||
|
||||
if ($response->successful()) {
|
||||
$checkout = $response->json();
|
||||
return redirect($checkout['redirectUrl']);
|
||||
} else {
|
||||
\Log::error('Yoco API Error', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body()
|
||||
]);
|
||||
return redirect()->route('checkout')->with('error', 'Unable to initialize payment: ' . $response->body());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Yoco Payment Exception', ['message' => $e->getMessage()]);
|
||||
return redirect()->route('checkout')->with('error', 'Payment error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function yocoSuccess(Order $order)
|
||||
{
|
||||
// Verify order is pending payment
|
||||
if ($order->payment_status === 'paid') {
|
||||
return redirect()->route('order-success', ['order' => $order])
|
||||
->with('success', 'Payment was already processed for this order.');
|
||||
}
|
||||
|
||||
// Update order status
|
||||
$order->update([
|
||||
'payment_status' => 'paid',
|
||||
'status' => 'processing',
|
||||
]);
|
||||
|
||||
// Reduce stock
|
||||
foreach ($order->items as $item) {
|
||||
$product = $item->product;
|
||||
$product->stock -= $item->quantity;
|
||||
$product->save();
|
||||
}
|
||||
|
||||
// Clear pending order from session
|
||||
session()->forget('pending_order_uuid');
|
||||
session()->forget('cart');
|
||||
|
||||
return redirect()->route('order-success', ['order' => $order])
|
||||
->with('success', 'Payment successful! Your order has been confirmed.');
|
||||
}
|
||||
|
||||
public function yocoCancel(Order $order)
|
||||
{
|
||||
return redirect()->route('checkout')
|
||||
->with('error', 'Payment was cancelled. Your order is still pending.');
|
||||
}
|
||||
|
||||
public function yocoFailure(Order $order)
|
||||
{
|
||||
$order->update([
|
||||
'payment_status' => 'failed',
|
||||
]);
|
||||
|
||||
return redirect()->route('checkout')
|
||||
->with('error', 'Payment failed. Please try again or use a different payment method.');
|
||||
}
|
||||
|
||||
public function yocoWebhook(Request $request)
|
||||
{
|
||||
// Verify webhook signature
|
||||
$payload = $request->getContent();
|
||||
$signature = $request->header('X-Yoco-Signature');
|
||||
|
||||
// Process webhook event
|
||||
$event = $request->all();
|
||||
|
||||
if (isset($event['type']) && $event['type'] === 'checkout.succeeded') {
|
||||
$metadata = $event['payload']['metadata'] ?? [];
|
||||
$orderId = $metadata['order_id'] ?? null;
|
||||
|
||||
if ($orderId) {
|
||||
$order = Order::find($orderId);
|
||||
if ($order && $order->payment_status !== 'paid') {
|
||||
$order->update([
|
||||
'payment_status' => 'paid',
|
||||
'status' => 'processing',
|
||||
]);
|
||||
|
||||
// Reduce stock
|
||||
foreach ($order->items as $item) {
|
||||
$product = $item->product;
|
||||
$product->stock -= $item->quantity;
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
public function show(Product $product)
|
||||
{
|
||||
return view('product-detail', ['product' => $product]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
use App\Models\HeroImage;
|
||||
|
||||
class WallpapersController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$products = Product::where('type', 'wallpaper')->get();
|
||||
$categories = Category::all();
|
||||
$heroImages = HeroImage::where('page', 'wallpaper')->where('is_active', true)->orderBy('sort_order')->get();
|
||||
|
||||
return view('wallpapers', [
|
||||
'products' => $products,
|
||||
'categories' => $categories,
|
||||
'heroImages' => $heroImages
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user