This commit is contained in:
2025-09-25 03:03:31 +05:00
commit ae480cf2f6
2768 changed files with 1485826 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
<?php
namespace App\Helpers\Ecommerce\Product\Filter;
use App\Rules\CommaSeparatedIntegers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ProductFilterer
{
/*
* Query builder
*/
protected $queryBuilder;
/**
* Request
*/
protected Request $request;
/**
* Product filterer
*/
public function __construct($queryBuilder, $request)
{
$this->queryBuilder = $queryBuilder;
$this->request = $request;
}
/**
* Create "new" class via static method
*/
public static function make($queryBuilder, $request): self
{
return new self($queryBuilder, $request);
}
/**
* Validate the request
*/
public function validateRequest(): \Illuminate\Contracts\Validation\Validator
{
return Validator::make($this->request->all(), [
'ids' => ['nullable', 'string', new CommaSeparatedIntegers()],
'brands' => ['nullable', 'string', new CommaSeparatedIntegers()],
'categories' => ['nullable', 'string', new CommaSeparatedIntegers()],
'name' => ['nullable', 'string', 'max:255'],
'min_price' => ['nullable', 'numeric'],
'max_price' => ['nullable', 'numeric'],
'backorder' => ['nullable', 'in:0,1'],
]);
}
/**
* Sort
*/
public function filter()
{
if ($this->validateRequest()->fails()) {
return $this->queryBuilder;
}
if ($this->request->filled('brands')) {
$this->queryBuilder->whereIntegerInRaw('products.brand_id', explode(',', $this->request->brands));
}
if ($this->request->filled('categories')) {
$this->queryBuilder->whereIn(
column: 'id',
values: (
fn ($query) => $query->from('product_has_relations')
->select('product_id')
->distinct('product_id')
->where('productable_type', '=', 'category')
->whereIn('productable_id', explode(',', $this->request->categories))
)
);
}
if ($this->request->filled('name')) {
$this->queryBuilder->where('products.name', 'ilike', $this->request->name.'%');
}
if ($this->request->filled('min_price')) {
$this->queryBuilder->whereRaw('products.price_amount::NUMERIC >= ?', [$this->request->float('min_price')]);
}
if ($this->request->filled('max_price')) {
$this->queryBuilder->whereRaw('products.price_amount::NUMERIC <= ?', [$this->request->float('max_price')]);
}
if ($this->request->filled('backorder')) {
$this->queryBuilder->where('products.backorder', $this->request->backorder);
}
return $this->queryBuilder;
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace App\Helpers\Ecommerce\Product\Sort;
use App\Rules\SortableColumnRule;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ProductSorter
{
/*
* Query builder
*/
protected mixed $queryBuilder;
/**
* Request
*/
protected Request $request;
/**
* Allowed columns for sorting
*
* @var array<string>
*/
protected array $allowedColumns = [
'price_amount',
'created_at',
'name',
];
/**
* Constructor
*/
public function __construct($queryBuilder, $request)
{
$this->queryBuilder = $queryBuilder;
$this->request = $request;
}
/**
* Construct statically
*/
public static function make($queryBuilder, $request): self
{
return new self($queryBuilder, $request);
}
/**
* Validate the request
*/
public function validateRequest(): \Illuminate\Contracts\Validation\Validator
{
return Validator::make($this->request->all(), [
'sorting' => ['nullable', 'string', new SortableColumnRule($this->allowedColumns)],
]);
}
/**
* Sort from request
*/
public function sort()
{
if ($this->request->isNotFilled('sorting') || $this->validateRequest()->fails()) {
return $this->orderByLatest();
}
return $this->queryBuilder->orderBy($this->getColumn(), $this->getSortType());
}
/**
* Get sort column
*/
public function getColumn(): string
{
return explode('-', $this->request->sorting)[0];
}
/**
* Get sort type
*/
public function getSortType(): string
{
$sortType = explode('-', $this->request->sorting)[1];
return $sortType === 'descending' ? 'DESC' : 'ASC';
}
/**
* Order by latest (created_at)
*/
public function orderByLatest(): mixed
{
return $this->queryBuilder->latest();
}
}

390
app/Helpers/helpers.php Normal file
View File

@@ -0,0 +1,390 @@
<?php
use App\Models\Auth\Verification;
use App\Models\Ecommerce\Channel\Channel;
use App\Models\Ecommerce\Product\Inventory\Inventory;
use App\Models\System\Settings\Settings;
use App\Repositories\Ecommerce\Product\Barcode\BarcodeRepository;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
if (! function_exists('modules_path')) {
/**
* Single translation for all locales
*/
function modules_path(string $path = ''): string
{
return app_path('Modules/'.$path);
}
}
if (! function_exists('translatable')) {
/**
* Single translation for all locales
*/
function translatable(string $text): array
{
return collect(config('app.locales'))->map(fn ($locale) => $text)->toArray();
}
}
if (! function_exists('removeWhiteSpace')) {
/**
* Remove white sapce from string
*/
function removeWhiteSpace(string $text): string
{
return preg_replace('/\s+/', '', $text);
}
}
if (! function_exists('temp_cache')) {
/**
* Temprory cache via array driver
*/
function temp_cache(string|array|null $key): mixed
{
if (is_string($key)) {
return cache()->get($key);
}
if (is_array($key)) {
return cache()->put(
key: $key[0],
value: $key[1]
);
}
return cache();
}
}
if (! function_exists('sendSMS')) {
/**
* Send a sms
*/
function sendSMS(string|int $phone, string|int $message): mixed
{
$response = Http::retry(
times: 3,
sleepMilliseconds: 50,
throw: false,
when: function (Exception $exception, PendingRequest $request) {
Log::channel('sms_api_error')
->error('Exception: ', [
'message' => $exception->getMessage(),
'line' => $exception->getLine(),
]);
return true;
})
->post('http://216.250.14.144:3000/api/data', [
'phone' => '+993'.$phone,
'code' => $message,
]);
return $response->body();
}
}
if (! function_exists('sendSMSVerification')) {
/**
* Send a sms verification code
*
* @return \App\Models\Verification | null
*/
function sendSMSVerification(string|int $phone_number): ?Verification
{
/* for apple testing */
$phone_code = ($phone_number == '61126667') ? 77777 : rand(10000, 99999);
$verification = Verification::where(['username' => $phone_number])->first();
$verification
? $verification->update(['code' => $phone_code])
: Verification::create(['username' => $phone_number, 'code' => $phone_code]);
sendSMS($phone_number, 'Tassyklaýyş belgi: '.$phone_code);
return $verification;
}
}
if (! function_exists('randomHex')) {
/**
* Generate random hex
*/
function randomHex(): string
{
$chars = 'ABCDEF0123456789';
$color = '#';
for ($i = 0; $i < 6; $i++) {
$color .= $chars[rand(0, strlen($chars) - 1)];
}
return $color;
}
}
if (! function_exists('phoneMaskFormat')) {
/**
* Phone number mask
*/
function phoneMaskFormat(): string
{
return '+(\\9\\93)-99-99-99-99';
}
}
if (! function_exists('unmask_phone')) {
/**
* Unmask phone from TM code +(993)-6x-xx-xx-xx to 6xxxxxxx
*/
function unmask_phone(string|int|float $phone): string
{
return str_replace(['+(993)', '-'], '', $phone);
}
}
if (! function_exists('mask_phone')) {
/**
* Mask phone for TM code from 6xxxxxxx to +(993)-6x-xx-xx-xx
*/
function mask_phone(string|int|float $phone): string
{
// Split the number into an array of every two digits
$chunks = array_chunk(str_split($phone, 2), 1);
// Collapse the array chunks into a string with dashes
return '+(993)-'.implode('-', array_map('implode', $chunks));
}
}
if (! function_exists('tmpostChannel')) {
/**
* Default channel
*
* @return \App\Models\Shop\Channel
*/
function tmpostChannel(): Channel
{
Cache::rememberForever('tmpostChannel', fn () => Channel::tmpostDefault());
return Cache::get('tmpostChannel');
}
}
if (! function_exists('tmpostDefaultInventory')) {
/**
* Default inventory id
*/
function tmpostDefaultInventory(): mixed
{
Cache::rememberForever('tmpostDefaultInventory', fn () => Inventory::tmpostDefault());
return Cache::get('tmpostDefaultInventory');
}
}
if (! function_exists('round_up')) {
/**
* Round up the float
*/
function round_up(string|int|float $number, int $precision = 2): float
{
$fig = (int) str_pad('1', $precision, '0');
return ceil($number * $fig) / $fig;
}
}
if (! function_exists('routeName')) {
/**
* Current route name
*/
function routeName(): ?string
{
return Route::currentRouteName();
}
}
if (! function_exists('routeIs')) {
/**
* Check if current route matches with given datas
*/
function routeIs(string|array $name): bool
{
return is_string($name)
? $name === routeName()
: in_array(routeName(), $name);
}
}
/**
* Check if route is product related
*/
function routeIsProductRelated(): bool
{
return routeIs([
'web.categories.products',
'web.brands.products',
'web.collections.products',
'web.entrepreneurs.show',
'web.search.index',
]);
}
if (! function_exists('keyValueExistsInArray')) {
/**
* Check "key-value" exists in array
*
* @param int $key
* @param int $value
*/
function keyValueExistsInArray(array $datas, string|int $key, string|int $value): bool
{
foreach ($datas as $data) {
if (isset($data[$key]) && $data[$key] === $value) {
return true;
}
}
return false;
}
}
if (! function_exists('productTaxForCategory')) {
/**
* Product tax for categories it has been related
* As of now, the highest category tax is taken
*/
function productTaxForCategory(array|Collection $categories): int
{
return DB::table('categories')
->whereIntegerInRaw('id', $categories)
->select(DB::raw('MAX(tax_percentage::integer) as tax'))
->pluck('tax')[0] ?? 1;
}
}
if (! function_exists('validateCommaSeperated')) {
/**
* Validates a comma-separated string of fields.
*
* - Validates the format of each field (alphanumeric or underscore).
* - Optionally checks fields against a model's fillable property.
*
* @param string|null $value The comma-separated string to validate.
* @param string|null $model The model class to check fields against (optional).
* @return string[] An array of validated fields.
*/
function validateCommaSeperated(?string $value, mixed $model = null): array
{
if (is_null($value)) {
return [];
}
$values = explode(',', removeWhiteSpace($value));
$validated = [];
$fields = [];
if (! is_null($model)) {
$fields = (new $model)->getFillable();
}
foreach ($values as $value) {
if (empty($value) || is_numeric($value)) {
continue;
}
if (! validator(data: ['key' => $value], rules: ['key' => ['alpha_dash:ascii']])->fails()) {
if (! empty($fields)) {
if (in_array($value, $fields)) {
$validated[] = $value;
}
continue;
}
$validated[] = $value;
}
}
return $validated;
}
}
if (! function_exists('orderAdminNumber')) {
/**
* Order admin phone number
*/
function orderAdminNumber(): int
{
return 65728952;
}
}
if (! function_exists('barcodeGeneratorRoute')) {
/**
* Barcode generator route
*/
function barcodeGeneratorRoute(): string
{
return BarcodeRepository::imageGeneratorRoute();
}
}
if (! function_exists('calculateProductPriceAmount')) {
/**
* Calculate product price amount
*/
function calculateProductPriceAmount(int|float $price, int $tax): float
{
return round_up(($price / (100 - $tax)) * 100);
}
}
/**
* Create halkbank order
*
* @param string $price
* @return array{status: string, url: string|null}
*/
function createHalkbankOrder($price = 123): array
{
$orderNumber = date('dmyHis');
$paymentResponse = Http::get('https://mpi.gov.tm/payment/rest/register.do', [
'orderNumber' => $orderNumber,
'amount' => $price,
'currency' => 934,
'language' => 'ru',
'userName' => 516122500260,
'password' => 'MrZsO9wfgWOBjf4',
'returnUrl' => route('online-payment-store'),
'pageView' => 'DESKTOP',
'description' => 'Kart tölegi',
])->onError(function ($response) {
Log::error('Payment error', [
'response' => [
'body' => $response->body(),
],
]);
});
if ($paymentResponse->failed()) {
return [
'status' => 'failed',
'url' => '',
];
}
return [
'status' => 'success',
'url' => $paymentResponse['formUrl'],
];
}