625 lines
25 KiB
PHP
625 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Area;
|
|
use App\Models\Category;
|
|
use App\Models\Setting;
|
|
use App\Services\CachingService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Str;
|
|
use JsonException;
|
|
|
|
class HelperService
|
|
{
|
|
public static function changeEnv($updateData = []): bool
|
|
{
|
|
if (count($updateData) > 0) {
|
|
// Read .env-file
|
|
$env = file_get_contents(base_path() . '/.env');
|
|
// Split string on every " " and write into array
|
|
// $env = explode(PHP_EOL, $env);
|
|
$env = preg_split('/\r\n|\r|\n/', $env);
|
|
$env_array = [];
|
|
foreach ($env as $env_value) {
|
|
if (empty($env_value)) {
|
|
// Add and Empty Line
|
|
$env_array[] = '';
|
|
|
|
continue;
|
|
}
|
|
|
|
$entry = explode('=', $env_value, 2);
|
|
$env_array[$entry[0]] = $entry[0] . '="' . str_replace('"', '', $entry[1]) . '"';
|
|
}
|
|
|
|
foreach ($updateData as $key => $value) {
|
|
$env_array[$key] = $key . '="' . str_replace('"', '', $value) . '"';
|
|
}
|
|
// Turn the array back to a String
|
|
$env = implode("\n", $env_array);
|
|
|
|
// And overwrite the .env with the new data
|
|
file_put_contents(base_path() . '/.env', $env);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @description - This function will return the nested category Option tags using in memory optimization
|
|
*/
|
|
public static function childCategoryRendering(&$categories, int $level = 0, ?string $parentCategoryID = ''): bool
|
|
{
|
|
// Foreach loop only on the parent category objects
|
|
foreach (collect($categories)->where('parent_category_id', $parentCategoryID) as $key => $category) {
|
|
echo "<option value='$category->id'>" . str_repeat(' ', $level * 4) . '|-- ' . $category->name . '</option>';
|
|
// Once the parent category object is rendered we can remove the category from the main object so that redundant data can be removed
|
|
$categories->forget($key);
|
|
|
|
// Now fetch the subcategories of the main category
|
|
$subcategories = $categories->where('parent_category_id', $category->id);
|
|
if (! empty($subcategories)) {
|
|
// Finally if subcategories are available then call the recursive function & see the magic
|
|
self::childCategoryRendering($categories, $level + 1, $category->id);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static function buildNestedChildSubcategoryObject($categories)
|
|
{
|
|
// Used json_decode & encode simultaneously because i wanted to convert whole nested array into object
|
|
try {
|
|
return json_decode(json_encode(self::buildNestedChildSubcategoryArray($categories), JSON_THROW_ON_ERROR), false, 512, JSON_THROW_ON_ERROR);
|
|
} catch (JsonException) {
|
|
return (object) [];
|
|
}
|
|
}
|
|
|
|
private static function buildNestedChildSubcategoryArray($categories)
|
|
{
|
|
$children = [];
|
|
// First Add Parent Categories to root level in an array
|
|
foreach ($categories->toArray() as $value) {
|
|
if ($value['parent_category_id'] == '') {
|
|
$children[] = $value;
|
|
}
|
|
}
|
|
|
|
// Then loop on the Parent Category to find the children categories
|
|
foreach ($children as $key => $value) {
|
|
$children[$key]['subcategories'] = self::findChildCategories($categories->toArray(), $value['id']);
|
|
}
|
|
|
|
return $children;
|
|
}
|
|
|
|
public static function findChildCategories($arr, $parent)
|
|
{
|
|
$children = [];
|
|
foreach ($arr as $key => $value) {
|
|
if ($value['parent_category_id'] == $parent) {
|
|
$children[] = $value;
|
|
}
|
|
}
|
|
foreach ($children as $key => $value) {
|
|
$children[$key]['subcategories'] = self::findChildCategories($arr, $value['id']);
|
|
}
|
|
|
|
return $children;
|
|
}
|
|
|
|
/*
|
|
* Sagar's Code :
|
|
* in this i have approached the reverse object moving & removing.
|
|
* which is not working as of now.
|
|
* but will continue working on this in future as it seems bit optimized approach from the current one
|
|
public static function buildNestedChildSubcategoryObject($categories, $finalCategories = []) {
|
|
echo "<pre>";
|
|
// Foreach loop only on the parent category objects
|
|
if (!empty($finalCategories)) {
|
|
$finalCategories = $categories->whereNull('parent_category_id');
|
|
}
|
|
foreach ($categories->whereNotNull('parent_category_id')->sortByDesc('parent_category_id') as $key => $category) {
|
|
echo "----------------------------------------------------------------------<br>";
|
|
$parentCategoryIndex = $categories->search(function ($data) use ($category) {
|
|
return $data['id'] == $category->parent_category_id;
|
|
});
|
|
if (!$parentCategoryIndex) {
|
|
continue;
|
|
}
|
|
// echo "*** This category will be moved to its parent category object ***<br>";
|
|
// print_r($category->toArray());
|
|
|
|
// Once the parent category object is rendered we can remove the category from the main object so that redundant data can be removed
|
|
$categories[$parentCategoryIndex]->subcategories[] = $category->toArray();
|
|
|
|
$categories->forget($key);
|
|
echo "<br>*** After all the operation main categories object will look like this ***<br>";
|
|
print_r($categories->toArray());
|
|
|
|
if (!empty($categories)) {
|
|
// Finally if subcategories are available then call the recursive function & see the magic
|
|
return self::buildNestedChildSubcategoryObject($categories, $finalCategories);
|
|
}
|
|
}
|
|
return $categories;
|
|
} */
|
|
|
|
public static function findParentCategory($category, $finalCategories = [])
|
|
{
|
|
$category = Category::find($category);
|
|
|
|
if (! empty($category)) {
|
|
$finalCategories[] = $category->id;
|
|
|
|
if (! empty($category->parent_category_id)) {
|
|
$finalCategories[] = self::findParentCategory($category->id, $finalCategories);
|
|
}
|
|
}
|
|
|
|
return $finalCategories;
|
|
}
|
|
|
|
/**
|
|
* Generate Slug for any model
|
|
*
|
|
* @param $model - Instance of Model
|
|
*/
|
|
public static function generateUniqueSlug($model, string $slug, ?int $excludeID = null, int $count = 0): string
|
|
{
|
|
/* NOTE : This can be improved by directly calling in the UI on type of title via AJAX */
|
|
$slug = Str::slug($slug);
|
|
$newSlug = $count ? $slug . '-' . $count : $slug;
|
|
|
|
$data = $model::where('slug', $newSlug);
|
|
if ($excludeID !== null) {
|
|
$data->where('id', '!=', $excludeID);
|
|
}
|
|
|
|
if (in_array(SoftDeletes::class, class_uses_recursive($model), true)) {
|
|
$data->withTrashed();
|
|
}
|
|
while ($data->exists()) {
|
|
return self::generateUniqueSlug($model, $slug, $excludeID, $count + 1);
|
|
}
|
|
|
|
return $newSlug;
|
|
}
|
|
|
|
public static function findAllCategoryIds($model): array
|
|
{
|
|
$ids = [];
|
|
|
|
foreach ($model as $item) {
|
|
$ids[] = $item['id'];
|
|
|
|
if (! empty($item['children'])) {
|
|
$ids = array_merge($ids, self::findAllCategoryIds($item['children']));
|
|
}
|
|
}
|
|
|
|
return $ids;
|
|
}
|
|
|
|
public static function generateRandomSlug($length = 10)
|
|
{
|
|
// Generate a random string of lowercase letters and numbers
|
|
$characters = 'abcdefghijklmnopqrstuvwxyz-';
|
|
$slug = '';
|
|
|
|
for ($i = 0; $i < $length; $i++) {
|
|
$index = rand(0, strlen($characters) - 1);
|
|
$slug .= $characters[$index];
|
|
}
|
|
|
|
return $slug;
|
|
}
|
|
|
|
/**
|
|
* Apply location filters to Item query with fallback logic
|
|
* Priority: area_id > city > state > country > latitude/longitude
|
|
*
|
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
* @param \Illuminate\Http\Request $request
|
|
* @param callable $applyAuthFilters Function to apply auth-specific filters
|
|
* @return array ['query' => $query, 'message' => $locationMessage]
|
|
*/
|
|
public static function applyLocationFilters($query, $request, $applyAuthFilters)
|
|
{
|
|
$isHomePage = $request->current_page === 'home';
|
|
$locationMessage = null;
|
|
$hasLocationFilter = $request->latitude !== null && $request->longitude !== null;
|
|
$hasCityFilter = !empty($request->city);
|
|
$hasStateFilter = !empty($request->state);
|
|
$hasCountryFilter = !empty($request->country);
|
|
$hasAreaFilter = !empty($request->area_id);
|
|
$hasAreaLocationFilter = !empty($request->area_latitude) && !empty($request->area_longitude);
|
|
|
|
$cityName = $request->city ?? null;
|
|
$stateName = $request->state ?? null;
|
|
$countryName = $request->country ?? null;
|
|
$areaId = $request->area_id ?? null;
|
|
|
|
$cityItemCount = 0;
|
|
$stateItemCount = 0;
|
|
$countryItemCount = 0;
|
|
$areaItemCount = 0;
|
|
$areaName = null;
|
|
|
|
// Handle area location filter (find closest area by lat/long)
|
|
if ($hasAreaLocationFilter && !$hasAreaFilter) {
|
|
$areaLat = $request->area_latitude;
|
|
$areaLng = $request->area_longitude;
|
|
$haversine = "(6371 * acos(cos(radians($areaLat))
|
|
* cos(radians(latitude))
|
|
* cos(radians(longitude) - radians($areaLng))
|
|
+ sin(radians($areaLat)) * sin(radians(latitude))))";
|
|
|
|
$closestArea = Area::whereNotNull('latitude')
|
|
->whereNotNull('longitude')
|
|
->selectRaw("areas.*, {$haversine} AS distance")
|
|
// ->orderBy('distance', 'asc')
|
|
|
|
->orderByRaw(
|
|
'(6371 * acos(
|
|
cos(radians(?)) *
|
|
cos(radians(latitude)) *
|
|
cos(radians(longitude) - radians(?)) +
|
|
sin(radians(?)) * sin(radians(latitude))
|
|
)) ASC',
|
|
[$areaLat, $areaLng, $areaLat]
|
|
)
|
|
|
|
->first();
|
|
|
|
if ($closestArea) {
|
|
$hasAreaFilter = true;
|
|
$areaId = $closestArea->id;
|
|
}
|
|
}
|
|
|
|
// Get area name if area filter is set
|
|
if ($hasAreaFilter) {
|
|
$area = Area::find($areaId);
|
|
$areaName = $area ? $area->name : __('the selected area');
|
|
}
|
|
|
|
// Save base query before location filters for fallback
|
|
$baseQueryBeforeLocation = clone $query;
|
|
|
|
// First, check for area filter (highest priority)
|
|
if ($hasAreaFilter) {
|
|
$areaQuery = clone $query;
|
|
$areaQuery->where('area_id', $areaId);
|
|
$areaQuery = $applyAuthFilters($areaQuery);
|
|
$areaItemExists = $areaQuery->exists();
|
|
|
|
if ($areaItemExists) {
|
|
$query = $areaQuery;
|
|
$areaItemCount = 1;
|
|
} else {
|
|
if ($isHomePage) {
|
|
$locationMessage = __('No Ads found in :area. Showing all available Ads.', ['area' => $areaName]);
|
|
} else {
|
|
$query = $areaQuery;
|
|
}
|
|
$areaItemCount = 0;
|
|
}
|
|
}
|
|
|
|
// Second, check for city filter
|
|
if ($hasCityFilter && (!$hasAreaFilter || $areaItemCount == 0)) {
|
|
$cityQuery = clone $query;
|
|
$cityQuery->where('city', $cityName);
|
|
$cityQuery = $applyAuthFilters($cityQuery);
|
|
$cityItemExists = $cityQuery->exists();
|
|
|
|
if ($cityItemExists) {
|
|
$query = $cityQuery;
|
|
$cityItemCount = 1;
|
|
if ($hasAreaFilter && $areaItemCount == 0 && $isHomePage) {
|
|
$locationMessage = __('No Ads found in :city. Showing all available Ads.', ['city' => $cityName]);
|
|
}
|
|
} else {
|
|
$cityItemCount = 0;
|
|
if ($isHomePage) {
|
|
if (!$locationMessage) {
|
|
$locationMessage = __('No Ads found in :city. Showing all available Ads.', ['city' => $cityName]);
|
|
} else {
|
|
$locationMessage = __('No Ads found in :area or :city. Showing all available Ads.', ['area' => $areaName, 'city' => $cityName]);
|
|
}
|
|
} else {
|
|
$query = $cityQuery;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Third, check for state filter
|
|
if ($hasStateFilter && (!$hasAreaFilter || $areaItemCount == 0) && (!$hasCityFilter || $cityItemCount == 0)) {
|
|
$stateQuery = clone $query;
|
|
$stateQuery->where('state', $stateName);
|
|
$stateQuery = $applyAuthFilters($stateQuery);
|
|
$stateItemExists = $stateQuery->exists();
|
|
|
|
if ($stateItemExists) {
|
|
$query = $stateQuery;
|
|
$stateItemCount = 1;
|
|
if (($hasAreaFilter && $areaItemCount == 0) || ($hasCityFilter && $cityItemCount == 0)) {
|
|
if ($isHomePage) {
|
|
$locationMessage = __('No Ads found in :state. Showing all available Ads.', ['state' => $stateName]);
|
|
}
|
|
}
|
|
} else {
|
|
$stateItemCount = 0;
|
|
if ($isHomePage) {
|
|
if (!$locationMessage) {
|
|
$locationMessage = __('No Ads found in :state. Showing all available Ads.', ['state' => $stateName]);
|
|
} else {
|
|
$parts = [];
|
|
if ($hasAreaFilter && $areaItemCount == 0) {
|
|
$parts[] = $areaName;
|
|
}
|
|
if ($hasCityFilter && $cityItemCount == 0) {
|
|
$parts[] = $cityName;
|
|
}
|
|
$parts[] = $stateName;
|
|
$locationMessage = __('No Ads found in :locations. Showing all available Ads.', ['locations' => implode(', ', $parts)]);
|
|
}
|
|
} else {
|
|
$query = $stateQuery;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fourth, check for country filter
|
|
if ($hasCountryFilter && (!$hasAreaFilter || $areaItemCount == 0) && (!$hasCityFilter || $cityItemCount == 0) && (!$hasStateFilter || $stateItemCount == 0)) {
|
|
$countryQuery = clone $query;
|
|
$countryQuery->where('country', $countryName);
|
|
$countryQuery = $applyAuthFilters($countryQuery);
|
|
$countryItemExists = $countryQuery->exists();
|
|
|
|
if ($countryItemExists) {
|
|
$query = $countryQuery;
|
|
$countryItemCount = 1;
|
|
if (($hasAreaFilter && $areaItemCount == 0) || ($hasCityFilter && $cityItemCount == 0) || ($hasStateFilter && $stateItemCount == 0)) {
|
|
if ($isHomePage) {
|
|
$locationMessage = __('No Ads found in :country. Showing all available Ads.', ['country' => $countryName]);
|
|
}
|
|
}
|
|
} else {
|
|
$countryItemCount = 0;
|
|
if ($isHomePage) {
|
|
if (!$locationMessage) {
|
|
$locationMessage = __('No Ads found in :country. Showing all available Ads.', ['country' => $countryName]);
|
|
} else {
|
|
$parts = [];
|
|
if ($hasAreaFilter && $areaItemCount == 0) {
|
|
$parts[] = $areaName;
|
|
}
|
|
if ($hasCityFilter && $cityItemCount == 0) {
|
|
$parts[] = $cityName;
|
|
}
|
|
if ($hasStateFilter && $stateItemCount == 0) {
|
|
$parts[] = $stateName;
|
|
}
|
|
$parts[] = $countryName;
|
|
$locationMessage = __('No Ads found in :locations. Showing all available Ads.', ['locations' => implode(', ', $parts)]);
|
|
}
|
|
} else {
|
|
$query = $countryQuery;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fifth, handle latitude/longitude location-based search
|
|
$hasHigherPriorityFilter = ($hasAreaFilter && $areaItemCount > 0) || ($hasCityFilter && $cityItemCount > 0) || ($hasStateFilter && $stateItemCount > 0) || ($hasCountryFilter && $countryItemCount > 0);
|
|
if ($hasLocationFilter && ((!$hasAreaFilter && !$hasCityFilter && !$hasStateFilter && !$hasCountryFilter) || $hasHigherPriorityFilter)) {
|
|
$latitude = $request->latitude;
|
|
$longitude = $request->longitude;
|
|
$requestedRadius = (float)($request->radius ?? null);
|
|
$exactLocationRadius = $request->radius;
|
|
|
|
$haversine = '(6371 * acos(cos(radians(?))
|
|
* cos(radians(latitude))
|
|
* cos(radians(longitude) - radians(?))
|
|
+ sin(radians(?)) * sin(radians(latitude))))';
|
|
|
|
$exactLocationQuery = clone $query;
|
|
$exactLocationQuery
|
|
->select('items.*')
|
|
->selectRaw("$haversine AS distance", [$latitude, $longitude, $latitude])
|
|
->where('latitude', '!=', 0)
|
|
->where('longitude', '!=', 0)
|
|
// CHANGE THIS: Use whereRaw instead of having to support pagination count
|
|
// Use <= so radius=0.0 still returns items at the exact same coordinates (distance 0)
|
|
->whereRaw("$haversine <= ?", [$latitude, $longitude, $latitude, $exactLocationRadius])
|
|
->orderBy('distance', 'asc');
|
|
|
|
if (Auth::check()) {
|
|
$exactLocationQuery->with(['item_offers' => function ($q) {
|
|
$q->where('buyer_id', Auth::user()->id);
|
|
}, 'user_reports' => function ($q) {
|
|
$q->where('user_id', Auth::user()->id);
|
|
}]);
|
|
|
|
$currentURI = explode('?', $request->getRequestUri(), 2);
|
|
if ($currentURI[0] == '/api/my-items') {
|
|
$exactLocationQuery->where(['user_id' => Auth::user()->id])->withTrashed();
|
|
} else {
|
|
$exactLocationQuery->where('status', 'approved')->has('user')->onlyNonBlockedUsers()->getNonExpiredItems();
|
|
}
|
|
} else {
|
|
$exactLocationQuery->where('status', 'approved')->getNonExpiredItems();
|
|
}
|
|
|
|
$exactLocationExists = $exactLocationQuery->exists();
|
|
|
|
if ($exactLocationExists) {
|
|
$query = $exactLocationQuery;
|
|
} else {
|
|
$searchRadius = $requestedRadius !== null && $requestedRadius > 0 ? $requestedRadius : 50;
|
|
|
|
$nearbyQuery = clone $query;
|
|
$nearbyQuery
|
|
->select('items.*')
|
|
->selectRaw("$haversine AS distance", [$latitude, $longitude, $latitude])
|
|
->where('latitude', '!=', 0)
|
|
->where('longitude', '!=', 0)
|
|
// CHANGE THIS: Use whereRaw instead of having
|
|
->whereRaw("$haversine < ?", [$latitude, $longitude, $latitude, $searchRadius])
|
|
->orderBy('distance', 'asc');
|
|
|
|
$nearbyQuery = $applyAuthFilters($nearbyQuery);
|
|
$nearbyItemExists = $nearbyQuery->exists();
|
|
|
|
if ($nearbyItemExists) {
|
|
$query = $nearbyQuery;
|
|
if (!$locationMessage) {
|
|
$locationMessage = __('No Ads found at your location. Showing nearby Ads.');
|
|
}
|
|
} else {
|
|
if ($isHomePage) {
|
|
$query = clone $baseQueryBeforeLocation;
|
|
if (!$locationMessage) {
|
|
$locationMessage = __('No Ads found at your location. Showing all available Ads.');
|
|
}
|
|
} else {
|
|
$query = $nearbyQuery;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['query' => $query, 'message' => $locationMessage];
|
|
}
|
|
|
|
/**
|
|
* Get watermark configuration status (enabled/disabled)
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function getWatermarkConfigStatus(): bool
|
|
{
|
|
$enabled = CachingService::getSystemSettings('watermark_enabled');
|
|
return !empty($enabled) && (int)$enabled === 1;
|
|
}
|
|
|
|
/**
|
|
* Get watermark configuration as decoded array
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getWatermarkConfigDecoded(): array
|
|
{
|
|
$settings = CachingService::getSystemSettings([
|
|
'watermark_enabled',
|
|
'watermark_image',
|
|
'watermark_opacity',
|
|
'watermark_size',
|
|
'watermark_style',
|
|
'watermark_position',
|
|
'watermark_rotation',
|
|
]);
|
|
|
|
return [
|
|
'enabled' => (int)($settings['watermark_enabled'] ?? 0),
|
|
'watermark_image' => $settings['watermark_image'] ?? null,
|
|
'opacity' => (int)($settings['watermark_opacity'] ?? 25),
|
|
'size' => (int)($settings['watermark_size'] ?? 10),
|
|
'style' => $settings['watermark_style'] ?? 'tile',
|
|
'position' => $settings['watermark_position'] ?? 'center',
|
|
'rotation' => (int)($settings['watermark_rotation'] ?? -30),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get a specific setting value
|
|
*
|
|
* @param string $key
|
|
* @return string|null
|
|
*/
|
|
public static function getSettingData(string $key): ?string
|
|
{
|
|
return CachingService::getSystemSettings($key);
|
|
}
|
|
|
|
/**
|
|
* Calculate item expiry date based on package listing duration
|
|
* Priority: listing_duration > package expiry > default
|
|
*
|
|
* @param \App\Models\Package|null $package
|
|
* @param \App\Models\UserPurchasedPackage|null $userPackage
|
|
* @return \Carbon\Carbon|null
|
|
*/
|
|
public static function calculateItemExpiryDate($package = null, $userPackage = null)
|
|
{
|
|
|
|
if (!$package) {
|
|
$freeAdUnlimited = Setting::where('name', 'free_ad_unlimited')->value('value') ?? 0;
|
|
$freeAdDays = Setting::where('name', 'free_ad_duration_days')->value('value') ?? 0;
|
|
// Unlimited free ads
|
|
if ((int) $freeAdUnlimited === 1) {
|
|
return null;
|
|
}
|
|
|
|
// Limited free ads
|
|
if (!empty($freeAdDays) && (int) $freeAdDays > 0) {
|
|
return Carbon::now()->addDays((int) $freeAdDays);
|
|
}
|
|
|
|
// Safety fallback (no expiry)
|
|
return null;
|
|
}
|
|
|
|
// ---------------------------------------
|
|
// PACKAGE LOGIC (existing, unchanged)
|
|
// ---------------------------------------
|
|
|
|
$listingDurationType = $package->listing_duration_type ?? null;
|
|
$listingDurationDays = $package->listing_duration_days ?? null;
|
|
|
|
// If listing_duration_type is null → use package expiry
|
|
if ($listingDurationType === null) {
|
|
if ($userPackage && $userPackage->end_date) {
|
|
return Carbon::parse($userPackage->end_date);
|
|
}
|
|
|
|
if ($package->duration === 'unlimited') {
|
|
return null;
|
|
}
|
|
|
|
return Carbon::now()->addDays((int) $package->duration);
|
|
}
|
|
|
|
if ($listingDurationType === 'package') {
|
|
if ($package->duration === 'unlimited') {
|
|
return null;
|
|
}
|
|
|
|
return Carbon::now()->addDays((int) $package->duration);
|
|
}
|
|
|
|
if ($listingDurationType === 'custom') {
|
|
if (!empty($listingDurationDays) && (int) $listingDurationDays > 0) {
|
|
return Carbon::now()->addDays((int) $listingDurationDays);
|
|
}
|
|
|
|
return Carbon::now()->addDays(30);
|
|
}
|
|
|
|
// Standard fallback
|
|
if (!empty($listingDurationDays) && (int) $listingDurationDays > 0) {
|
|
return Carbon::now()->addDays((int) $listingDurationDays);
|
|
}
|
|
|
|
return Carbon::now()->addDays(30);
|
|
}
|
|
}
|