reupload
This commit is contained in:
107
app/Helpers/Ecommerce/Product/Filter/ProductFilterer.php
Normal file
107
app/Helpers/Ecommerce/Product/Filter/ProductFilterer.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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')) {
|
||||
$operator = $this->queryBuilder->getConnection()->getDriverName() === 'pgsql' ? 'ilike' : 'like';
|
||||
$this->queryBuilder->where('products.name', $operator, $this->request->name.'%');
|
||||
}
|
||||
|
||||
if ($this->request->filled('min_price')) {
|
||||
if ($this->queryBuilder->getConnection()->getDriverName() === 'pgsql') {
|
||||
$this->queryBuilder->whereRaw('products.price_amount::NUMERIC >= ?', [$this->request->float('min_price')]);
|
||||
} else {
|
||||
$this->queryBuilder->where('products.price_amount', '>=', $this->request->float('min_price'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->filled('max_price')) {
|
||||
if ($this->queryBuilder->getConnection()->getDriverName() === 'pgsql') {
|
||||
$this->queryBuilder->whereRaw('products.price_amount::NUMERIC <= ?', [$this->request->float('max_price')]);
|
||||
} else {
|
||||
$this->queryBuilder->where('products.price_amount', '<=', $this->request->float('max_price'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->filled('backorder')) {
|
||||
$this->queryBuilder->where('products.backorder', $this->request->backorder);
|
||||
}
|
||||
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
}
|
||||
96
app/Helpers/Ecommerce/Product/Sort/ProductSorter.php
Normal file
96
app/Helpers/Ecommerce/Product/Sort/ProductSorter.php
Normal 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();
|
||||
}
|
||||
}
|
||||
396
app/Helpers/helpers.php
Normal file
396
app/Helpers/helpers.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Auth\Verification;
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Models\Ecommerce\Product\Inventory\Inventory;
|
||||
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
|
||||
{
|
||||
if (! app()->isProduction()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
// $response = Http::withToken('WsTEJTBGlQiJ')
|
||||
// ->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('https://sms.post.gov.tm/api/clients/sms/create', [
|
||||
// 'phone' => '993'.$phone,
|
||||
// 'content' => $message,
|
||||
// ]);
|
||||
|
||||
// return $response->json();
|
||||
}
|
||||
}
|
||||
|
||||
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'],
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user