苏木 6f945d2a79 Merge remote-tracking branch 'origin/main' into fix/sumu-product-prices
# Conflicts:
#	app/Http/Controllers/Business/Pages.php
#	modules/EcommerceFinance/Services/JianSunSnapshotRefresher.php
#	tests/Unit/EcommerceFinance/JianSunSnapshotRefresherTest.php
2026-07-30 18:07:38 +08:00

820 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Business;
use App\Models\Banking\MarketingAccountBill;
use App\Models\StoreReceivables\StoreZhongcaiReconciliation;
use App\Utilities\SidebarPermissions;
use Illuminate\Routing\Controller;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Modules\EcommerceFinance\Repositories\EdgeBandingRepository;
use Modules\EcommerceFinance\Repositories\InventoryRepository;
use Modules\EcommerceFinance\Repositories\PinduoduoSpreadBillRepository;
use Modules\EcommerceFinance\Repositories\ProductPriceRepository;
use Modules\EcommerceFinance\Repositories\RawBoardPayablesRepository;
use Modules\EcommerceFinance\Repositories\SupplierPayableReportRepository;
use Modules\EcommerceFinance\Repositories\TmallTaobaoSpreadBillRepository;
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Throwable;
class Pages extends Controller
{
private const EDGE_BANDING_SOURCE = 'jiansun';
private const INVENTORY_SOURCE = 'jiansun';
private const RAW_BOARD_SOURCE = 'jiansun';
public function __construct(
private readonly EdgeBandingRepository $edgeBanding,
private readonly InventoryRepository $inventory,
private readonly ProductPriceRepository $productPrices,
private readonly RawBoardPayablesRepository $rawBoardPayables,
private readonly SupplierPayableReportRepository $supplierPayableReports,
private readonly UpstreamQueryCacheFactory $upstreamCaches,
) {
}
/**
* 支持的营销账户平台列表
*/
protected const MARKETING_PLATFORMS = [
'taobao-alipay', 'taobao-wechat', 'pinduoduo',
'jd-wallet', 'douyin', 'dewu', 'vipshop',
];
public function home()
{
$section = collect(SidebarPermissions::navigationFor(user()))->first();
abort_if(empty($section), 403, '当前账号没有已授权的侧边栏功能。');
$page = collect($section['pages'] ?? [])->first();
$parameters = ['section' => $section['slug']];
if ($page) {
$parameters['page'] = $page['slug'];
if (! empty($page['tabs'])) {
$parameters['tab'] = $page['tabs'][0]['slug'];
}
}
return redirect()->route('business.pages.show', $parameters);
}
public function show(string $section, ?string $page = null, ?string $tab = null)
{
$sectionConfig = collect(SidebarPermissions::navigationFor(user()))->firstWhere('slug', $section);
abort_if(empty($sectionConfig), 403, '当前账号没有该页面权限。');
$pages = collect($sectionConfig['pages'] ?? []);
if ($pages->isEmpty()) {
abort_if($page !== null || $tab !== null, 404);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => null,
'tabs' => collect(),
'activeTab' => null,
]);
}
if ($page === null) {
return $this->redirectToPage($sectionConfig, $pages->first());
}
$pageConfig = $pages
->flatMap(fn (array $configuredPage) => [
$configuredPage,
...($configuredPage['children'] ?? []),
])
->firstWhere('slug', $page);
abort_if(empty($pageConfig), 404);
// 如果该 page 是一个 children-only 的父级分组,访问它时跳到第一个子页面
if (! empty($pageConfig['children']) && empty($pageConfig['tabs'])) {
return $this->redirectToPage($sectionConfig, $pageConfig['children'][0]);
}
$tabs = collect($pageConfig['tabs'] ?? []);
if ($tabs->isEmpty()) {
abort_if($tab !== null, 404);
$content = $this->resolveContent($page, null);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => $pageConfig,
'tabs' => $tabs,
'activeTab' => null,
'content' => $content,
'edgeBanding' => $this->edgeBandingContent($page),
'inventoryData' => $this->inventoryContent($page),
'rawBoardData' => $this->rawBoardContent($page),
]);
}
if ($tab === null) {
return $this->redirectToPage($sectionConfig, $pageConfig);
}
$activeTab = $tabs->firstWhere('slug', $tab);
abort_if(empty($activeTab), 404);
$content = $this->resolveContent($page, $tab);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => $pageConfig,
'tabs' => $tabs,
'activeTab' => $activeTab,
'content' => $content,
'edgeBanding' => $this->edgeBandingContent($page),
'inventoryData' => $this->inventoryContent($page),
'rawBoardData' => $this->rawBoardContent($page),
]);
}
/**
* 根据页面和标签加载对应的业务内容数据
*/
protected function resolveContent(string $page, ?string $tab): ?array
{
return match ($page) {
'marketing-account-bills' => $this->contentForMarketingAccountBills($tab),
'zhongcai-reconciliation' => $this->contentForZhongcaiReconciliation(),
'product-prices' => $this->contentForProductPrices(),
'board-deductions' => $this->contentForBoardDeductions(),
'supplier-payables-report' => $this->contentForSupplierPayablesReport(),
default => null,
};
}
protected function contentForBoardDeductions(): array
{
$filters = [
'stock_in_date' => $this->dateFilter(request('stock_in_date')),
'stock_out_date' => $this->dateFilter(request('stock_out_date')),
'furniture_factory' => mb_substr(trim((string) request('furniture_factory', '')), 0, 191),
'lamination_factory' => mb_substr(trim((string) request('lamination_factory', '')), 0, 191),
'deduction_type' => mb_substr(trim((string) request('deduction_type', '')), 0, 191),
'material_name' => mb_substr(trim((string) request('material_name', '')), 0, 191),
'claim_number' => mb_substr(trim((string) request('claim_number', '')), 0, 191),
];
$page = max((int) request('page', 1), 1);
$limit = min(max((int) setting('default.list_limit', 25), 1), 100);
$cache = $this->upstreamCaches->forSource('jiansun');
try {
$result = $cache->remember(
'supplier-payables.board-deductions.list',
['filters' => $filters, 'page' => $page, 'limit' => $limit],
fn () => $this->supplierPayableReports->boardDeductions($filters, $page, $limit),
);
return [
'boardDeductions' => $this->supplierPayablePaginator((array) ($result['rows'] ?? []), $limit, $page),
'boardDeductionFilters' => $filters,
'boardDeductionStatus' => $cache->responseHeaders(),
'boardDeductionError' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return [
'boardDeductions' => $this->supplierPayablePaginator([], $limit, $page),
'boardDeductionFilters' => $filters,
'boardDeductionStatus' => [],
'boardDeductionError' => $exception->getMessage(),
];
} catch (Throwable $exception) {
report($exception);
return [
'boardDeductions' => $this->supplierPayablePaginator([], $limit, $page),
'boardDeductionFilters' => $filters,
'boardDeductionStatus' => [],
'boardDeductionError' => '板材扣款数据暂时无法读取,请稍后重试。',
];
}
}
protected function contentForSupplierPayablesReport(): array
{
$month = trim((string) request('month', ''));
$filters = [
'month' => preg_match('/^\d{4}-\d{2}$/', $month) === 1 ? $month : '',
'supplier' => mb_substr(trim((string) request('supplier', '')), 0, 191),
'checker' => mb_substr(trim((string) request('checker', '')), 0, 191),
'settlement_method' => mb_substr(trim((string) request('settlement_method', '')), 0, 191),
];
$page = max((int) request('page', 1), 1);
$limit = min(max((int) setting('default.list_limit', 25), 1), 100);
$cache = $this->upstreamCaches->forSource('jiansun');
try {
$result = $cache->remember(
'supplier-payables.report.list',
['filters' => $filters, 'page' => $page, 'limit' => $limit],
fn () => $this->supplierPayableReports->supplierPayables($filters, $page, $limit),
);
return [
'supplierPayables' => $this->supplierPayablePaginator((array) ($result['rows'] ?? []), $limit, $page),
'supplierPayableFilters' => $filters,
'supplierPayableStatus' => $cache->responseHeaders(),
'supplierPayableError' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return [
'supplierPayables' => $this->supplierPayablePaginator([], $limit, $page),
'supplierPayableFilters' => $filters,
'supplierPayableStatus' => [],
'supplierPayableError' => $exception->getMessage(),
];
} catch (Throwable $exception) {
report($exception);
return [
'supplierPayables' => $this->supplierPayablePaginator([], $limit, $page),
'supplierPayableFilters' => $filters,
'supplierPayableStatus' => [],
'supplierPayableError' => '供应商应付数据暂时无法读取,请稍后重试。',
];
}
}
private function supplierPayablePaginator(array $rows, int $limit, int $page): Paginator
{
return new Paginator(
Collection::make($rows)->map(static fn (array $row): object => (object) $row),
$limit,
$page,
[
'path' => request()->url(),
'query' => request()->except('page'),
],
);
}
protected function contentForProductPrices(): array
{
$priceType = trim((string) request('price_type', ''));
$costReduction = trim((string) request('cost_reduction', ''));
$filters = [
'barcode' => mb_substr(trim((string) request('barcode', '')), 0, 191),
'price_type' => in_array($priceType, ['pricing', 'repricing'], true) ? $priceType : '',
'cost_reduction' => in_array($costReduction, ['yes', 'no'], true) ? $costReduction : '',
'effective_from' => $this->dateFilter(request('effective_from')),
'effective_to' => $this->dateFilter(request('effective_to')),
];
$page = max((int) request('page', 1), 1);
$limit = min(max((int) setting('default.list_limit', 25), 1), 100);
$cache = $this->upstreamCaches->forSource('jiansun');
try {
$result = $cache->remember(
'supplier-payables.product-prices.list',
['filters' => $filters, 'page' => $page, 'limit' => $limit],
fn () => $this->productPrices->paginate($filters, $page, $limit),
);
return [
'productPrices' => $this->productPricePaginator((array) ($result['rows'] ?? []), $limit, $page),
'productPriceFilters' => $filters,
'productPriceStatus' => $cache->responseHeaders(),
'productPriceError' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return [
'productPrices' => $this->productPricePaginator([], $limit, $page),
'productPriceFilters' => $filters,
'productPriceStatus' => [],
'productPriceError' => $exception->getMessage(),
];
} catch (Throwable $exception) {
report($exception);
return [
'productPrices' => $this->productPricePaginator([], $limit, $page),
'productPriceFilters' => $filters,
'productPriceStatus' => [],
'productPriceError' => '商品价格暂时无法读取,请稍后重试。',
];
}
}
private function productPricePaginator(array $rows, int $limit, int $page): Paginator
{
return new Paginator(
Collection::make($rows)->map(static fn (array $row): object => (object) $row),
$limit,
$page,
[
'path' => request()->url(),
'query' => request()->except('page'),
],
);
}
/**
* 封边条页面只读取上游快照;未启用或无快照时将错误交给页面显示,
* 避免工作台因上游暂不可用直接返回 500。
*/
protected function edgeBandingContent(string $page): ?array
{
$scope = match ($page) {
'edge-banding-prices' => 'edge-banding.prices',
'details' => 'edge-banding.details',
'edge-banding-payables' => 'edge-banding.payables',
default => null,
};
if ($scope === null) {
return null;
}
$filters = match ($page) {
'edge-banding-prices' => $this->edgeBandingFilters([
'raw_name', 'raw_code', 'production_code', 'supplier',
]),
'details' => $this->edgeBandingFilters([
'date', 'production_code', 'raw_name', 'return_flag',
'outbound_supplier', 'arrival_supplier', 'outbound_no',
]),
'edge-banding-payables' => $this->edgeBandingFilters(['period', 'supplier']),
default => [],
};
if ($page === 'edge-banding-payables' && ! isset($filters['period'])) {
$filters['period'] = '2026-06';
}
$pageNumber = max((int) request('data_page', 1), 1);
$limit = min(max((int) request('data_limit', 25), 1), 100);
$cache = $this->upstreamCaches->forSource(self::EDGE_BANDING_SOURCE);
try {
$result = $cache->remember(
$scope,
['filters' => $filters, 'page' => $pageNumber, 'limit' => $limit],
fn () => match ($scope) {
'edge-banding.prices' => $this->edgeBanding->prices($filters, $pageNumber, $limit),
'edge-banding.details' => $this->edgeBanding->details($filters, $pageNumber, $limit),
'edge-banding.payables' => $this->edgeBanding->payables($filters, $pageNumber, $limit),
},
);
return [
'filters' => $filters,
'result' => $result,
'page' => $pageNumber,
'limit' => $limit,
'last_page' => max((int) ceil(max((int) ($result['total'] ?? 0), 0) / $limit), 1),
'status' => $cache->responseHeaders(),
'error' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return [
'filters' => $filters,
'result' => ['data' => [], 'total' => 0],
'page' => $pageNumber,
'limit' => $limit,
'last_page' => 1,
'status' => [],
'error' => $exception->getMessage(),
];
} catch (Throwable $exception) {
report($exception);
return [
'filters' => $filters,
'result' => ['data' => [], 'total' => 0],
'page' => $pageNumber,
'limit' => $limit,
'last_page' => 1,
'status' => [],
'error' => '封边条数据暂时无法读取,请稍后重试。',
];
}
}
private function edgeBandingFilters(array $fields): array
{
$filters = [];
foreach ($fields as $field) {
$value = trim((string) request($field, ''));
if ($value !== '') {
$filters[$field] = $value;
}
}
return $filters;
}
/**
* 素板价格与对账页面使用 JianSun 只读查询,并保留共享快照链路。
*/
protected function rawBoardContent(string $page): ?array
{
$scope = match ($page) {
'board-prices' => 'raw-board.prices',
'board-reconciliation' => 'raw-board.reconciliations',
default => null,
};
if ($scope === null) {
return null;
}
$filters = $scope === 'raw-board.prices'
? $this->rawBoardFilters(['raw_name', 'raw_code', 'production_code', 'supplier'])
: $this->rawBoardReconciliationFilters();
$pageNumber = max((int) request('data_page', 1), 1);
$limit = min(max((int) request('data_limit', 25), 1), 100);
try {
$cache = $this->upstreamCaches->forSource(self::RAW_BOARD_SOURCE);
$result = $cache->remember(
$scope,
['filters' => $filters, 'page' => $pageNumber, 'limit' => $limit],
fn () => $scope === 'raw-board.prices'
? $this->rawBoardPayables->prices($filters, $pageNumber, $limit)
: $this->rawBoardPayables->reconciliations($filters, $pageNumber, $limit),
);
return [
'filters' => $filters,
'result' => $result,
'page' => $pageNumber,
'limit' => $limit,
'last_page' => max((int) ceil(max((int) ($result['total'] ?? 0), 0) / $limit), 1),
'status' => $cache->responseHeaders(),
'error' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return $this->emptyRawBoardContent($filters, $pageNumber, $limit, $exception->getMessage());
} catch (Throwable $exception) {
report($exception);
return $this->emptyRawBoardContent(
$filters,
$pageNumber,
$limit,
'素板应付数据暂时无法读取,请稍后重试。',
);
}
}
private function rawBoardFilters(array $fields): array
{
$filters = [];
foreach ($fields as $field) {
$value = mb_substr(trim((string) request($field, '')), 0, 100);
if ($value !== '') {
$filters[$field] = $value;
}
}
return $filters;
}
private function rawBoardReconciliationFilters(): array
{
$filters = $this->rawBoardFilters([
'production_code', 'raw_name', 'outbound_supplier',
'arrival_supplier', 'outbound_no',
]);
foreach (['date_from', 'date_to'] as $field) {
$value = $this->dateFilter(request($field));
if ($value !== '') {
$filters[$field] = $value;
}
}
$returnFlag = trim((string) request('return_flag', ''));
if (in_array($returnFlag, ['normal', 'return'], true)) {
$filters['return_flag'] = $returnFlag;
}
return $filters;
}
private function emptyRawBoardContent(array $filters, int $page, int $limit, string $error): array
{
return [
'filters' => $filters,
'result' => ['data' => [], 'total' => 0, 'totals' => []],
'page' => $page,
'limit' => $limit,
'last_page' => 1,
'status' => [],
'error' => $error,
];
}
/**
* 板材库存页面使用 JianSun 最新素板快照,并保留生产共享快照链路。
*/
protected function inventoryContent(string $page): ?array
{
if ($page !== 'board-inventory-details') {
return null;
}
$filters = $this->inventoryFilters();
$pageNumber = max((int) request('data_page', 1), 1);
$limit = min(max((int) request('data_limit', 25), 1), 100);
try {
$cache = $this->upstreamCaches->forSource(self::INVENTORY_SOURCE);
$result = $cache->remember(
'inventory.board-details',
['filters' => $filters, 'page' => $pageNumber, 'limit' => $limit],
fn () => $this->inventory->boardInventoryDetails($filters, $pageNumber, $limit),
);
return [
'filters' => $filters,
'result' => $result,
'page' => $pageNumber,
'limit' => $limit,
'last_page' => max((int) ceil(max((int) ($result['total'] ?? 0), 0) / $limit), 1),
'status' => $cache->responseHeaders(),
'error' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return $this->emptyInventoryContent($filters, $pageNumber, $limit, $exception->getMessage());
} catch (Throwable $exception) {
report($exception);
return $this->emptyInventoryContent(
$filters,
$pageNumber,
$limit,
'板材库存数据暂时无法读取,请稍后重试。',
);
}
}
private function inventoryFilters(): array
{
$filters = [];
foreach (['manufacturer', 'specification', 'matching_value'] as $field) {
$value = mb_substr(trim((string) request($field, '')), 0, 100);
if ($value !== '') {
$filters[$field] = $value;
}
}
foreach (['date_from', 'date_to'] as $field) {
$value = $this->dateFilter(request($field));
if ($value !== '') {
$filters[$field] = $value;
}
}
return $filters;
}
private function emptyInventoryContent(array $filters, int $page, int $limit, string $error): array
{
return [
'filters' => $filters,
'result' => ['data' => [], 'total' => 0, 'totals' => []],
'page' => $page,
'limit' => $limit,
'last_page' => 1,
'status' => [],
'error' => $error,
];
}
/**
* 种菜应收对账 - 加载对账数据(21字段)
*/
protected function contentForZhongcaiReconciliation(): ?array
{
return [
'view' => 'store_receivables.zhongcai_reconciliations.partials.embedded-table',
'records' => StoreZhongcaiReconciliation::orderBy('bill_date', 'desc')
->orderBy('id', 'desc')
->paginate(setting('default.list_limit', 25)),
];
}
/**
* 营销账户账单 - 加载指定平台的数据
* 淘系支付宝、拼多多使用独立的流水账单表,其他平台使用通用汇总账单表
*/
protected function contentForMarketingAccountBills(string $tab): ?array
{
if (!in_array($tab, self::MARKETING_PLATFORMS, true)) {
return null;
}
// 淘系支付宝 - 从上游 JianSun.tmall_spread_bills 读取并按表1字段展示(9列)
if ($tab === 'taobao-alipay') {
$request = request();
$storeName = mb_substr(trim((string) $request->query('store_name')), 0, 191);
$fromDate = $this->dateFilter($request->query('from'));
$toDate = $this->dateFilter($request->query('to'));
$flowType = trim((string) $request->query('flow_type'));
$flowType = in_array($flowType, ['income', 'expense'], true) ? $flowType : '';
$page = max(1, (int) $request->query('page', 1));
$pageSize = min(max((int) setting('default.list_limit', 25), 1), 100);
$repository = app(TmallTaobaoSpreadBillRepository::class);
$cache = $this->upstreamCaches->forSource('jiansun');
$filters = [
'store_name' => $storeName !== '' ? $storeName : null,
'from' => $fromDate !== '' ? $fromDate : null,
'to' => $toDate !== '' ? $toDate : null,
'flow_type' => $flowType !== '' ? $flowType : null,
];
$result = $cache->remember(
'taobao-alipay-bills.list',
['filters' => $filters, 'page' => $page, 'limit' => $pageSize],
fn () => $repository->paginate(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type'],
$page,
$pageSize
)
);
$summary = $cache->remember(
'taobao-alipay-bills.summary',
['filters' => $filters],
fn () => $repository->summary(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type']
)
);
$storeOptions = $cache->remember(
'taobao-alipay-bills.options',
[],
fn () => $repository->storeOptions()
);
$bills = new LengthAwarePaginator(
Collection::make($result['rows']),
$result['total'],
$pageSize,
$page,
[
'path' => $request->url(),
'query' => $request->query(),
]
);
return [
'view' => 'banking.marketing_taobao_alipay_bills.partials.embedded-table',
'bills' => $bills,
'filters' => [
'store_name' => $storeName,
'from' => $fromDate,
'to' => $toDate,
'flow_type' => $flowType,
'store_options' => $storeOptions,
],
'summary' => $summary,
];
}
// 拼多多 - 从上游 JianSun.pdd_spread_bills 读取并按表1字段展示(7列)
// 表1列:店铺名称 | 金额 | 余额 | 摘要 | 收支类型 | 资金类型 | 费用时间
if ($tab === 'pinduoduo') {
$request = request();
$storeName = mb_substr(trim((string) $request->query('store_name')), 0, 191);
$fromDate = $this->dateFilter($request->query('from'));
$toDate = $this->dateFilter($request->query('to'));
$flowType = trim((string) $request->query('flow_type'));
$flowType = in_array($flowType, ['income', 'expense'], true) ? $flowType : '';
$page = max(1, (int) $request->query('page', 1));
$pageSize = min(max((int) setting('default.list_limit', 25), 1), 100);
$repository = app(PinduoduoSpreadBillRepository::class);
$cache = $this->upstreamCaches->forSource('jiansun');
$filters = [
'store_name' => $storeName !== '' ? $storeName : null,
'from' => $fromDate !== '' ? $fromDate : null,
'to' => $toDate !== '' ? $toDate : null,
'flow_type' => $flowType !== '' ? $flowType : null,
];
// 页面只读取共享查询快照;冷查询由统一限流和熔断链路访问上游。
$result = $cache->remember(
'pinduoduo-bills.list',
['filters' => $filters, 'page' => $page, 'limit' => $pageSize],
fn () => $repository->paginate(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type'],
$page,
$pageSize
)
);
$summary = $cache->remember(
'pinduoduo-bills.summary',
['filters' => $filters],
fn () => $repository->summary(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type']
)
);
$storeOptions = $cache->remember(
'pinduoduo-bills.options',
[],
fn () => $repository->storeOptions()
);
// 包装成 Laravel 分页器,便于 Blade 用 $bills->links()
$bills = new LengthAwarePaginator(
Collection::make($result['rows']),
$result['total'],
$pageSize,
$page,
[
'path' => $request->url(),
'query' => $request->query(),
]
);
return [
'view' => 'banking.marketing_pinduoduo_bills.partials.embedded-table',
'bills' => $bills,
'filters' => [
'store_name' => $storeName,
'from' => $fromDate,
'to' => $toDate,
'flow_type' => $flowType,
'store_options' => $storeOptions,
],
'summary' => $summary,
];
}
// 其他平台 - 使用通用汇总账单表(15字段)
return [
'view' => 'banking.marketing_account_bills.partials.embedded-table',
'platform' => $tab,
'accounts' => MarketingAccountBill::platform($tab)
->orderBy('bill_date', 'desc')
->orderBy('id', 'desc')
->paginate(setting('default.list_limit', 25)),
];
}
protected function redirectToPage(array $section, array $page)
{
$parameters = [
'section' => $section['slug'],
'page' => $page['slug'],
];
if (! empty($page['tabs'])) {
$parameters['tab'] = $page['tabs'][0]['slug'];
}
return redirect()->route('business.pages.show', $parameters);
}
private function dateFilter(mixed $value): string
{
$date = trim((string) $value);
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) !== 1) {
return '';
}
[$year, $month, $day] = array_map('intval', explode('-', $date));
return checkdate($month, $day, $year) ? $date : '';
}
}