wip
This commit is contained in:
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
61
.env.example
Normal file
61
.env.example
Normal file
@@ -0,0 +1,61 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
API_TOKEN=123
|
||||
NOVA_LICENSE_KEY=UEkhFwqhhYw2UPqcoVNWWhKrZOOWXujgj7pLPdDzMqflYX4Pwl
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=postshop
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_HOST=
|
||||
PUSHER_PORT=443
|
||||
PUSHER_SCHEME=https
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
28
.eslintrc
Normal file
28
.eslintrc
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
};
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/.phpunit.cache
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
auth.json
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/.fleet
|
||||
/.idea
|
||||
/.vscode
|
||||
/nova
|
||||
/nova-old
|
||||
/dump.rdb
|
||||
**/.DS_Store
|
||||
51
README.md
Normal file
51
README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Postshop
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
# Install posgres and configure
|
||||
sudo su - postgres
|
||||
psql
|
||||
CREATE USER postshop WITH PASSWORD 'ACvL2(F@H^F)D7gs';
|
||||
CREATE DATABASE postshopdb WITH OWNER = postshop LC_COLLATE = 'en_US.UTF-8' TEMPLATE template0;
|
||||
|
||||
# Clone
|
||||
git clone --depth 1 https://github.com/nurmuhammet-ali/postshop.git
|
||||
|
||||
# Install packages and configure
|
||||
cd postshop
|
||||
cp .env.example .env
|
||||
composer install --prefer-dist
|
||||
vim .env
|
||||
# setup db
|
||||
# ...
|
||||
|
||||
# Link the storage
|
||||
php artisan storage:link
|
||||
|
||||
# Migrate and seed
|
||||
php artisan migrate --seed
|
||||
|
||||
# Setup permessions (nginx)
|
||||
chown -R www-data:www-data ./
|
||||
sudo find -type f -exec chmod 644 {} \;
|
||||
sudo find -type d -exec chmod 755 {} \;
|
||||
sudo chgrp -R www-data storage bootstrap/cache
|
||||
sudo chmod -R ug+rwx storage bootstrap/cache
|
||||
|
||||
# Setup supervisor
|
||||
apt-get install supervisor
|
||||
vim /etc/supervisor/conf.d/laravel-default-worker.conf
|
||||
[program:laravel-default-worker]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=php /var/www/artisan queue:work --sleep=3 --tries=3 --max-time=3600
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=www-data
|
||||
numprocs=8
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/www/postshop/storage/logs/worker.log
|
||||
stopwaitsecs=3600
|
||||
|
||||
sudo supervisorctl reread
|
||||
sudo supervisorctl update
|
||||
sudo supervisorctl start laravel-default-worker:*
|
||||
32
app/Console/Kernel.php
Normal file
32
app/Console/Kernel.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
use Laravel\Nova\Fields\Attachments\PruneStaleAttachments;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// Remove unnecessary documents...
|
||||
$schedule->exec('rm -rf '.public_path('app-docs/*'))->everyMinute();
|
||||
|
||||
// Remove non saved attachments...
|
||||
$schedule->call(new PruneStaleAttachments)->daily();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
22
app/Events/Ecommerce/Channel/ChannelActiveStateChanged.php
Normal file
22
app/Events/Ecommerce/Channel/ChannelActiveStateChanged.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events\Ecommerce\Channel;
|
||||
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChannelActiveStateChanged implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public $channel
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
20
app/Events/Ecommerce/Product/Category/CategoryTaxChanged.php
Normal file
20
app/Events/Ecommerce/Product/Category/CategoryTaxChanged.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events\Ecommerce\Product\Category;
|
||||
|
||||
use App\Models\Ecommerce\Product\Category\Category;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CategoryTaxChanged
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Category $category
|
||||
) {}
|
||||
}
|
||||
22
app/Events/Ecommerce/Product/Order/OrderCreated.php
Normal file
22
app/Events/Ecommerce/Product/Order/OrderCreated.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events\Ecommerce\Product\Order;
|
||||
|
||||
use App\Models\Ecommerce\Product\Order\Order;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderCreated
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Order $order
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
33
app/Exceptions/Handler.php
Normal file
33
app/Exceptions/Handler.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// 404..
|
||||
$this->renderable(function (NotFoundHttpException $e, $request) {
|
||||
if ($request->is('api/*')) {
|
||||
return response()->noContent(404);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
75
app/Exports/Ecommerce/Product/Order/ExportOrderReport.php
Normal file
75
app/Exports/Ecommerce/Product/Order/ExportOrderReport.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports\Ecommerce\Product\Order;
|
||||
|
||||
use App\Models\Ecommerce\Product\Order\OrderItem;
|
||||
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
|
||||
class ExportOrderReport implements FromQuery, ShouldAutoSize, WithHeadings, WithMapping
|
||||
{
|
||||
use Exportable;
|
||||
|
||||
/**
|
||||
* Get the start & end date to filter
|
||||
*/
|
||||
public function __construct(
|
||||
public string $start_date,
|
||||
public string $end_date
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return OrderItem::join(
|
||||
table: 'orders',
|
||||
first: fn ($join) => $join->on('order_items.order_id', '=', 'orders.id')
|
||||
->where('orders.status', '=', OrderStatus::COMPLETED)
|
||||
->whereNull('deleted_at')
|
||||
)->whereRaw('(orders.created_at >= ? AND orders.created_at <= ?)', [
|
||||
$this->start_date.' 00:00:00',
|
||||
$this->end_date.' 23:59:59',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Order
|
||||
*/
|
||||
public function map($order): array
|
||||
{
|
||||
$orderDeliveryTime = match ($order->shipping_method) {
|
||||
'express' => 'Ekspres',
|
||||
default => $order->time,
|
||||
};
|
||||
|
||||
return [
|
||||
$order->order_id,
|
||||
$order->name,
|
||||
$orderDeliveryTime,
|
||||
$order->quantity,
|
||||
$order->unit_price_amount,
|
||||
$order->unit_price_amount * $order->quantity,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Headings for stylesheet
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'Sargyt ID',
|
||||
'Ady',
|
||||
'Sene',
|
||||
'Sany',
|
||||
'Satylan Bahasy',
|
||||
'Satylan umumy bahasy',
|
||||
];
|
||||
}
|
||||
}
|
||||
98
app/Helpers/Ecommerce/Product/Filter/ProductFilterer.php
Normal file
98
app/Helpers/Ecommerce/Product/Filter/ProductFilterer.php
Normal 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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
390
app/Helpers/helpers.php
Normal file
390
app/Helpers/helpers.php
Normal 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'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\System\VersionManagement;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\System\VersionManagement\CheckForUpdateRequest;
|
||||
use App\Models\System\VersionManagement\AppVersion;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class AppVersionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Check for app updates
|
||||
*/
|
||||
public function checkForUpdate(CheckForUpdateRequest $request): JsonResponse
|
||||
{
|
||||
$app_version = AppVersion::latest()->where('os', $request->os)->first();
|
||||
|
||||
if (! $app_version || $request->version === $app_version->version) {
|
||||
return $this->latestVersion();
|
||||
}
|
||||
|
||||
if ($request->version < $app_version->version && $app_version->important) {
|
||||
return $this->requiredToUpdate();
|
||||
}
|
||||
|
||||
if ((int) $request->version < (int) $app_version->version) {
|
||||
return $this->optionalToUpdate();
|
||||
}
|
||||
|
||||
return $this->versionNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Latest version
|
||||
*/
|
||||
public function latestVersion(): JsonResponse
|
||||
{
|
||||
return response()->json(['update' => 'latest']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to update
|
||||
*/
|
||||
public function requiredToUpdate(): JsonResponse
|
||||
{
|
||||
return response()->json(['update' => 'required']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update not required, but should be
|
||||
*/
|
||||
public function optionalToUpdate(): JsonResponse
|
||||
{
|
||||
return response()->json(['update' => 'optional']);
|
||||
}
|
||||
|
||||
/**
|
||||
* App version not found
|
||||
*/
|
||||
public function versionNotFound(): JsonResponse
|
||||
{
|
||||
return response()->json(['update' => 'version-not-found']);
|
||||
}
|
||||
}
|
||||
23
app/Http/Controllers/Api/V1/AttributeController.php
Normal file
23
app/Http/Controllers/Api/V1/AttributeController.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shop\Product\Attribute;
|
||||
|
||||
class AttributeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return response()->rest(
|
||||
Attribute::with('values:id,key,attribute_id,value,position')
|
||||
->where('is_enabled', true)
|
||||
->get(['id', 'slug', 'name', 'type', 'category_id', 'is_enabled'])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Auth\Register;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AuthRegisterRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'phone_number' => ['required', 'integer', 'between:61000000,71999999', 'unique:users,phone_number'],
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'address' => ['required', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a passed validation attempt.
|
||||
*/
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
$name = explode(' ', $this->name);
|
||||
|
||||
$this->merge([
|
||||
'first_name' => $name[0],
|
||||
'last_name' => count($name) > 1 ? $name[0] : '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
80
app/Http/Controllers/Api/V1/AuthController.php
Normal file
80
app/Http/Controllers/Api/V1/AuthController.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Auth\Register\AuthRegisterRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Auth\AuthLoginRequest;
|
||||
use App\Http\Requests\Api\V1\Auth\AuthVerifyRequest;
|
||||
use App\Models\User;
|
||||
use App\Repositories\UserRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Auth) Guest token (walk-in-user)
|
||||
*/
|
||||
public function guestToken(): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
data: UserRepository::guestUser()->createToken(bin2hex(random_bytes(25)))->plainTextToken,
|
||||
code: 201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user
|
||||
*/
|
||||
public function register(AuthRegisterRequest $request): JsonResponse
|
||||
{
|
||||
UserRepository::registerUser($request)();
|
||||
|
||||
sendSMSVerification($request->phone_number);
|
||||
|
||||
return response()->rest(
|
||||
data: [],
|
||||
code: 201,
|
||||
message: sprintf('%s: %s', __('Verification code sent to'), $request->phone_number)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Auth) Login
|
||||
*/
|
||||
public function login(AuthLoginRequest $request): JsonResponse
|
||||
{
|
||||
sendSMSVerification($request->phone_number);
|
||||
|
||||
return response()->rest(
|
||||
data: [],
|
||||
code: 201,
|
||||
message: sprintf('%s: %s', __('Verification code sent to'), $request->phone_number)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Auth) Verify the code
|
||||
*/
|
||||
public function verify(AuthVerifyRequest $request): JsonResponse
|
||||
{
|
||||
$user = User::where('phone_number', $request->phone_number)->firstOr(UserRepository::registerUser($request));
|
||||
|
||||
return response()->rest(
|
||||
data: $user->createToken(bin2hex(random_bytes(20)))->plainTextToken,
|
||||
code: 201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Auth)* Delete user
|
||||
*
|
||||
* @authenticated
|
||||
*/
|
||||
public function delete(): JsonResponse
|
||||
{
|
||||
auth()->user()->delete();
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
32
app/Http/Controllers/Api/V1/Banner/BannerController.php
Normal file
32
app/Http/Controllers/Api/V1/Banner/BannerController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Banner;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Banner\Requests\BannerIndexRequest;
|
||||
use App\Http\Controllers\Api\V1\Banner\Resources\BannerResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CMS\Media\Banner;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class BannerController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Media) Banners (index)
|
||||
*/
|
||||
public function index(BannerIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
BannerResource::collection(
|
||||
Banner::with('media')
|
||||
->where('is_visible', true)
|
||||
->when($request->place, fn ($query, $place) => $query->where('place', $place))
|
||||
->when($request->app, fn ($query, $app) => $query->where('app', $app))
|
||||
->ordered()
|
||||
->simplePaginate(
|
||||
perPage: $request->perPage,
|
||||
columns: $request->fields,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Banner\Requests;
|
||||
|
||||
use App\Models\CMS\Media\Banner;
|
||||
use App\Models\System\Settings\OS;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class BannerIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'place' => ['nullable', 'string', Rule::in(array_keys(Banner::places()))],
|
||||
'app' => ['nullable', 'string', Rule::in(array_keys(OS::apps()))],
|
||||
'perPage' => ['nullable', 'integer'],
|
||||
'fields' => ['nullable', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a passed validation attempt.
|
||||
*/
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
$fields = [];
|
||||
if ($this->fields) {
|
||||
$sanitezedFields = validateCommaSeperated($this->fields, Banner::class);
|
||||
|
||||
$fields['fields'] = ! empty($sanitezedFields) ? $sanitezedFields : ['*'];
|
||||
} else {
|
||||
$fields['fields'] = ['*'];
|
||||
}
|
||||
|
||||
$this->merge([
|
||||
'perPage' => $this->perPage ?: 6,
|
||||
...$fields,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'place.in' => sprintf('Valid sources: %s', implode(', ', array_keys(OS::apps()))),
|
||||
'app.in' => sprintf('Valid sources: %s', implode(', ', array_keys(OS::apps()))),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Banner\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BannerResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->whenHas('id'),
|
||||
'app' => $this->whenHas('app'),
|
||||
'title' => $this->whenHas('title'),
|
||||
'place' => $this->whenHas('place'),
|
||||
'link' => $this->whenHas('link'),
|
||||
'resource_type' => $this->whenHas('resource_type'),
|
||||
'resource_id' => $this->whenHas('resource_id'),
|
||||
'thumbnail' => $this->thumbnail('350x350'),
|
||||
'related_resources' => $this->when(
|
||||
condition: $this->shouldIncludeRelatedResources($request),
|
||||
value: fn () => $this->relatedResources(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should include related resources
|
||||
*/
|
||||
protected function shouldIncludeRelatedResources(Request $request): bool
|
||||
{
|
||||
if (! $request->fields) {
|
||||
$request->merge(['fields' => ['*']]);
|
||||
}
|
||||
|
||||
if ($request->fields[0] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array(['resource_type', 'resource_id', 'place'], $request->fields);
|
||||
}
|
||||
}
|
||||
61
app/Http/Controllers/Api/V1/Brand/BrandController.php
Normal file
61
app/Http/Controllers/Api/V1/Brand/BrandController.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Brand;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Brand\Resources\BrandResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Brand\BrandIndexRequest;
|
||||
use App\Http\Requests\Api\V1\Brand\BrandProductsRequest;
|
||||
use App\Models\Ecommerce\Product\Brand\Brand;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Brands (index)
|
||||
*/
|
||||
public function index(BrandIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
BrandResource::collection(
|
||||
Brand::query()
|
||||
->with('media')
|
||||
->enabled()
|
||||
->ordered()
|
||||
->when($request->input('type'), fn ($query, $type) => $query->where('type', $type))
|
||||
->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands (show)
|
||||
*
|
||||
* @param App\Models\Ecommerce\Product\Brand\Brand $brand
|
||||
*/
|
||||
public function show(Brand $brand): JsonResponse
|
||||
{
|
||||
$brand->load('media');
|
||||
|
||||
return response()->rest(new BrandResource($brand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands (products)
|
||||
*/
|
||||
public function products(BrandProductsRequest $request, Brand $brand): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ProductResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->queryAsFromResource($brand)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Brand\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BrandMediaResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'thumbnail' => $this->getUrl('thumb200x200'),
|
||||
'images_400x400' => $this->getUrl('thumb400x400'),
|
||||
'images_720x720' => $this->getUrl('thumb720x720'),
|
||||
'images_800x800' => $this->getUrl('thumb800x800'),
|
||||
'images_1200x1200' => $this->getUrl('thumb1200x1200'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Brand\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BrandResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'type' => $this->type,
|
||||
'description' => $this->description,
|
||||
'media' => BrandMediaResource::collection($this->whenLoaded('media')),
|
||||
];
|
||||
}
|
||||
}
|
||||
32
app/Http/Controllers/Api/V1/Carousel/CarouselController.php
Normal file
32
app/Http/Controllers/Api/V1/Carousel/CarouselController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Carousel;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Carousel\Requests\CarouselIndexRequest;
|
||||
use App\Http\Controllers\Api\V1\Carousel\Resources\CarouselResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CMS\Media\Carousel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class CarouselController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Media) Carousels (index)
|
||||
*/
|
||||
public function index(CarouselIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
CarouselResource::collection(
|
||||
Carousel::with('media')
|
||||
->where('is_visible', true)
|
||||
->when($request->place, fn ($query, $place) => $query->where('place', $place))
|
||||
->when($request->app, fn ($query, $app) => $query->where('app', $app))
|
||||
->ordered()
|
||||
->simplePaginate(
|
||||
perPage: $request->perPage,
|
||||
columns: $request->fields,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Carousel\Requests;
|
||||
|
||||
use App\Models\CMS\Media\Carousel;
|
||||
use App\Models\System\Settings\OS;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CarouselIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'place' => ['nullable', 'string', Rule::in(array_keys(
|
||||
Carousel::places()
|
||||
))],
|
||||
'perPage' => ['nullable', 'integer'],
|
||||
'fields' => ['nullable', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a passed validation attempt.
|
||||
*/
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
$fields = [];
|
||||
if ($this->fields) {
|
||||
$sanitezedFields = validateCommaSeperated($this->fields, Carousel::class);
|
||||
|
||||
$fields['fields'] = ! empty($sanitezedFields) ? $sanitezedFields : ['*'];
|
||||
} else {
|
||||
$fields['fields'] = ['*'];
|
||||
}
|
||||
|
||||
$this->merge([
|
||||
'perPage' => $this->perPage ?: 6,
|
||||
...$fields,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'place.in' => sprintf('Valid sources: %s', implode(', ', array_keys(OS::apps()))),
|
||||
'app.in' => sprintf('Valid sources: %s', implode(', ', array_keys(OS::apps()))),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Carousel\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class CarouselResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->whenHas('id'),
|
||||
'title' => $this->whenHas('title'),
|
||||
'place' => $this->whenHas('place'),
|
||||
'app' => $this->whenHas('app'),
|
||||
'link' => $this->whenHas('link'),
|
||||
'resource_id' => $this->whenHas('resource_id'),
|
||||
'resource_type' => $this->whenHas('resource_type'),
|
||||
'thumbnail' => $this->thumbnail('650x650'),
|
||||
'image' => $this->getFirstMediaUrl('main'),
|
||||
];
|
||||
}
|
||||
}
|
||||
79
app/Http/Controllers/Api/V1/CartController.php
Normal file
79
app/Http/Controllers/Api/V1/CartController.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Cart\CartRemoveRequest;
|
||||
use App\Http\Requests\Api\V1\Cart\CartStoreRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class CartController extends Controller
|
||||
{
|
||||
/**
|
||||
* (*) Users carts (index)
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$cartItems = auth()->user()->carts()
|
||||
->with(['product' => ['media', 'brand']])
|
||||
->orderBy('cart_items.id', 'desc')
|
||||
->get()
|
||||
->each(function ($cartItem) {
|
||||
if ($cartItem->product->stock < $cartItem->product_quantity) {
|
||||
if ($cartItem->product->stock >= 1) {
|
||||
$cartItem->update([
|
||||
'product_quantity' => $cartItem->product->stock,
|
||||
]);
|
||||
} else {
|
||||
$cartItem->delete();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return response()->rest(
|
||||
$cartItems->map(fn ($cartItem) => [
|
||||
'id' => $cartItem->id,
|
||||
'user_id' => $cartItem->user_id,
|
||||
'product_id' => $cartItem->product_id,
|
||||
'product_quantity' => $cartItem->product_quantity,
|
||||
'created_at' => $cartItem->created_at,
|
||||
'updated_at' => $cartItem->updated_at,
|
||||
'product' => new ProductResource($cartItem->product),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Store new cart (store)
|
||||
*/
|
||||
public function store(CartStoreRequest $request): JsonResponse
|
||||
{
|
||||
auth()->user()->carts()->updateOrInsert(
|
||||
['user_id' => auth()->id(), 'product_id' => $request->product_id],
|
||||
['product_quantity' => $request->product_quantity]
|
||||
);
|
||||
|
||||
return response()->rest(message: 'Cart item added or updated successfully', code: 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Remove item from cart (patch)
|
||||
*/
|
||||
public function remove(CartRemoveRequest $request): JsonResponse
|
||||
{
|
||||
auth()->user()->carts()->where('product_id', $request->product_id)->delete();
|
||||
|
||||
return response()->rest(message: 'Cart item removed successfully', code: 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Delete all user's carts (delete)
|
||||
*/
|
||||
public function destroy(): JsonResponse
|
||||
{
|
||||
auth()->user()->carts()->delete();
|
||||
|
||||
return response()->rest(message: "All user's carts deleted successfully", code: 200);
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/Api/V1/Category/CategoryController.php
Normal file
73
app/Http/Controllers/Api/V1/Category/CategoryController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Category;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Category\Resources\CategoryResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Category\CategoryIndexRequest;
|
||||
use App\Http\Requests\Api\V1\Product\BasicProductIndexRequest;
|
||||
use App\Models\Ecommerce\Product\Category\Category;
|
||||
use App\Repositories\Ecommerce\Product\Category\CategoryRepository;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Categories (index)
|
||||
*/
|
||||
public function index(CategoryIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
CategoryResource::collection(
|
||||
CategoryRepository::make($request)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories (show)
|
||||
*/
|
||||
public function show(Category $category): JsonResponse
|
||||
{
|
||||
$category->load(['media', 'children']);
|
||||
|
||||
return response()->rest(new CategoryResource($category));
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories (children)
|
||||
*/
|
||||
public function children(Category $category): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
CategoryResource::collection(
|
||||
$category->children()
|
||||
->enabled()
|
||||
->ordered()
|
||||
->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands (products)
|
||||
*/
|
||||
public function products(BasicProductIndexRequest $request, Category $category): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ProductResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->queryAsFromResource($category)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Category\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class CategoryMediaResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'thumbnail' => $this->getUrl(),
|
||||
'images_400x400' => $this->getUrl('thumb400x400'),
|
||||
'images_720x720' => $this->getUrl('thumb720x720'),
|
||||
'images_800x800' => $this->getUrl('thumb800x800'),
|
||||
'images_1200x1200' => $this->getUrl('thumb1200x1200'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Category\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class CategoryResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'parent_id' => $this->parent_id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'group' => $this->type,
|
||||
'description' => $this->description,
|
||||
'color' => $this->color,
|
||||
'media' => CategoryMediaResource::collection($this->whenLoaded('media')),
|
||||
'children' => static::collection($this->whenLoaded('children')),
|
||||
];
|
||||
}
|
||||
}
|
||||
61
app/Http/Controllers/Api/V1/Channel/ChannelController.php
Normal file
61
app/Http/Controllers/Api/V1/Channel/ChannelController.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Channel;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Channel\Requests\ChannelIndexRequest;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\V1\Channel\ChannelResource;
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ChannelController extends Controller
|
||||
{
|
||||
/**
|
||||
* Channels (index)
|
||||
*/
|
||||
public function index(ChannelIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ChannelResource::collection(
|
||||
Channel::query()
|
||||
->with('media')
|
||||
->where('is_visible', true)
|
||||
->ordered()
|
||||
->simplePaginate(
|
||||
perPage: $request->perPage,
|
||||
columns: $request->fields,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels (show)
|
||||
*/
|
||||
public function show(Channel $channel): JsonResponse
|
||||
{
|
||||
$channel->load('media');
|
||||
|
||||
return response()->rest(new ChannelResource($channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels (products)
|
||||
*/
|
||||
public function products(Request $request, Channel $channel): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ProductResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->queryAsFromResource($channel)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Channel\Requests;
|
||||
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ChannelIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'perPage' => ['nullable', 'integer'],
|
||||
'fields' => ['nullable', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a passed validation attempt.
|
||||
*/
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
$fields = [];
|
||||
if ($this->fields) {
|
||||
$sanitezedFields = validateCommaSeperated($this->fields, Channel::class);
|
||||
|
||||
$fields['fields'] = ! empty($sanitezedFields) ? $sanitezedFields : ['*'];
|
||||
} else {
|
||||
$fields['fields'] = ['*'];
|
||||
}
|
||||
|
||||
$this->merge([
|
||||
'perPage' => $this->perPage ?: 6,
|
||||
...$fields,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Collection;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Collection\Resources\CollectionResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Product\BasicProductIndexRequest;
|
||||
use App\Models\Ecommerce\Product\Collection\Collection;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CollectionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Collections (index).
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
CollectionResource::collection(
|
||||
Collection::with('media')->where('is_visible', true)->ordered()->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated
|
||||
*/
|
||||
public function paginated(Request $request)
|
||||
{
|
||||
$perPage = $request->perPage ?: 6;
|
||||
|
||||
return response()->rest_paginate(
|
||||
CollectionResource::collection(
|
||||
Collection::with('media')->where('is_visible', true)->ordered()->simplePaginate($perPage)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collections (show)
|
||||
*/
|
||||
public function show(Collection $collection): JsonResponse
|
||||
{
|
||||
$collection->load('media');
|
||||
|
||||
return response()->rest(new CollectionResource($collection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collections (products)
|
||||
*/
|
||||
public function products(BasicProductIndexRequest $request, Collection $collection)
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ProductResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->queryAsFromResource($collection)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Collection\Resources;
|
||||
|
||||
use App\Http\Resources\MediaResource;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class CollectionResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'description' => $this->description,
|
||||
'sort_order' => $this->sort_order,
|
||||
'seo_title' => $this->seo_title,
|
||||
'seo_description' => $this->seo_description,
|
||||
'media' => MediaResource::collection($this->whenLoaded('media')),
|
||||
];
|
||||
}
|
||||
}
|
||||
102
app/Http/Controllers/Api/V1/CommentController.php
Normal file
102
app/Http/Controllers/Api/V1/CommentController.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shop\Product\Product;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return response()->rest(
|
||||
DB::table('comments')->where('user_id', auth()->id())->get()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'product_id' => ['required', 'integer', 'exists:products,id'],
|
||||
'comment' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Validation failed');
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
$product = Product::find($request->product_id);
|
||||
$product->commentAsUser($user, strip_tags($request->comment));
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'comment_id' => ['required', 'integer', 'exists:comments,id'],
|
||||
'comment' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, []);
|
||||
}
|
||||
|
||||
DB::table('comments')->where('id', $request->comment_id)->update([
|
||||
'comment' => strip_tags($request->comment),
|
||||
]);
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), ['comment_id' => ['required', 'integer']]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, []);
|
||||
}
|
||||
|
||||
DB::table('comments')->where('id', $request->comment_id)->delete();
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/Api/V1/ContactMessageController.php
Normal file
27
app/Http/Controllers/Api/V1/ContactMessageController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Forms\ContactUS\ContactUSStoreRequest;
|
||||
use App\Models\CMS\Forms\ContactUS;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ContactMessageController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Marketing)* Contact us
|
||||
*/
|
||||
public function store(ContactUSStoreRequest $request): JsonResponse
|
||||
{
|
||||
ContactUS::create([
|
||||
'phone' => $request->phone,
|
||||
'title' => $request->title,
|
||||
'content' => $request->content,
|
||||
'type' => $request->type,
|
||||
'user_id' => auth()->id(),
|
||||
]);
|
||||
|
||||
return response()->rest([], 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Auth\Verification;
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class EntrepreneurAuthController extends Controller
|
||||
{
|
||||
public function register(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'first_name' => ['required', 'string'],
|
||||
'phone_number' => ['required', 'integer', 'between:61000000,71999999', 'unique:users,phone_number'],
|
||||
'email' => ['required', 'email', 'unique:users,email'],
|
||||
'password' => ['required'],
|
||||
'region' => ['required', 'string', 'in:mr,ag,ah,dz,lb,bn'],
|
||||
'patent_data' => ['nullable'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($request) {
|
||||
$user = User::create([
|
||||
'first_name' => $request->first_name,
|
||||
'last_name' => ' ',
|
||||
'email' => $request->email,
|
||||
'password' => bcrypt($request->password),
|
||||
'phone_number' => $request->phone_number,
|
||||
]);
|
||||
|
||||
$user->documents()->create([
|
||||
'patent_data' => str_replace('public/', '', $request->file('patent_data')?->store('public/entrepreneur/patent_data') ?? 'public/'),
|
||||
]);
|
||||
|
||||
sendSMSVerification($request->phone_number);
|
||||
|
||||
// Verification::updateOrCreate(['username' => $request->email, 'code' => 12345]);
|
||||
});
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
public function verifyPhoneNumber(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), ['phone_number' => 'required|integer|between:61000000,65999999', 'code' => 'required|string']);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$verification = Verification::where('username', $request->phone_number)->where('code', $request->code)->first();
|
||||
|
||||
if (! $verification) {
|
||||
return response()->rest([], 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
User::where('phone_number', $request->phone_number)->update(['phone_number_verified_at' => now()]);
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
public function verifyEmail(Request $request)
|
||||
{
|
||||
// email should be validated for deleted_at to
|
||||
$validator = Validator::make($request->all(), ['email' => ['required', 'string', 'email', 'exists:users,email'], 'code' => ['required', 'string']]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$verfication = Verification::where('username', $request->email)->where('code', $request->code)->first();
|
||||
|
||||
if (! $verfication) {
|
||||
return response()->rest([], 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
$user->email_verified_at = now();
|
||||
$user->save();
|
||||
|
||||
event(new Verified($user));
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
public function finalize(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'email' => ['required', 'string', 'email', 'exists:users,email'],
|
||||
'password' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
if (! auth()->attempt(['email' => $request->email, 'password' => $request->password])) {
|
||||
return response()->rest([], 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||
return response()->rest([], 400, 'Failed');
|
||||
}
|
||||
|
||||
if (! $user->hasRole('vendor')) {
|
||||
$user->assignRole('vendor');
|
||||
|
||||
// User
|
||||
$name = $user->first_name;
|
||||
|
||||
$channel = Channel::create([
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name).'_'.random_int(10000, 9999999),
|
||||
'description' => '',
|
||||
'timezone' => 'asia/ashgabat',
|
||||
'url' => url('/'),
|
||||
'is_default' => true,
|
||||
'channelables_type' => 'App\Models\User',
|
||||
'channelables_id' => $user->id,
|
||||
'is_visible' => true,
|
||||
]);
|
||||
|
||||
$channel->inventories()->create([
|
||||
'name' => $name,
|
||||
'code' => Str::slug($name).'_'.random_int(10000, 9999999),
|
||||
'region' => 'ag',
|
||||
'shareable' => false,
|
||||
'is_default' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$bearerToken = $user->createToken(bin2hex(random_bytes(20)))->plainTextToken;
|
||||
|
||||
return response()->rest($bearerToken, 201);
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), ['email' => 'required|string|email', 'password' => 'required|string']);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Absolutely wrong credentials.');
|
||||
}
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||
return response()->rest([], 400, 'Failed');
|
||||
}
|
||||
|
||||
$bearerToken = $user->createToken(bin2hex(random_bytes(20)))->plainTextToken;
|
||||
|
||||
return response()->rest($bearerToken, 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\V1\Vendor\Order\VendorOrderIndexResource;
|
||||
use App\Http\Resources\Api\V1\Vendor\Order\VendorOrderShowResource;
|
||||
use App\Models\Ecommerce\Product\Order\Order;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EntrepreneurOrderController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$order_ids = DB::table('order_items')
|
||||
->where('channel_id', auth()->user()->channel()->id)
|
||||
->distinct()
|
||||
->pluck('order_id');
|
||||
|
||||
$perPage = $request->input('perPage') ?? 20;
|
||||
$page = $request->input('page') ?? 1;
|
||||
|
||||
return response()->rest_paginate(
|
||||
VendorOrderIndexResource::collection(
|
||||
Order::query()
|
||||
->whereIntegerInRaw('orders.id', $order_ids)
|
||||
->with('paymentType')
|
||||
->latest()
|
||||
->paginate($perPage, ['*'], 'page', $page)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function show(Order $order)
|
||||
{
|
||||
$order->load(['items' => ['product' => ['media']]]);
|
||||
|
||||
return new VendorOrderShowResource($order);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class VendorProductStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// essentials...
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string', 'max:255'],
|
||||
'files' => ['nullable'],
|
||||
|
||||
// prices...
|
||||
'cost_amount' => ['required', 'numeric'],
|
||||
'old_price_amount' => ['nullable', 'numeric', 'gt:cost_amount'],
|
||||
|
||||
// relationships...
|
||||
// 'integer', 'exists:brands,id'
|
||||
'brand_id' => ['nullable'],
|
||||
'category_ids' => ['required', 'array'],
|
||||
'collection_ids' => ['nullable', 'array'],
|
||||
|
||||
// inventories...
|
||||
'stock' => ['required', 'integer', 'min:1'],
|
||||
'sku' => ['nullable', 'string', 'max:255'],
|
||||
'barcode' => ['nullable', 'string', 'max:255', 'unique:products,barcode'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class VendorProductUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// essentials...
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string', 'max:255'],
|
||||
'new_imgs' => ['nullable'],
|
||||
'deleted_images' => ['nullable'],
|
||||
|
||||
// prices...
|
||||
'cost_amount' => ['required', 'numeric'],
|
||||
'old_price_amount' => ['nullable', 'numeric', 'gt:cost_amount'],
|
||||
|
||||
// relationships...
|
||||
'brand_id' => ['nullable', 'integer', 'exists:brands,id'],
|
||||
'category_ids' => ['required', 'array'],
|
||||
'collection_ids' => ['nullable', 'array'],
|
||||
|
||||
// inventories...
|
||||
'stock' => ['required', 'integer', 'min:1'],
|
||||
'sku' => ['nullable', 'string', 'max:255'],
|
||||
'barcode' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class VendorMetricsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$channel = auth()->user()->channel();
|
||||
|
||||
$products_count = $channel->products()->count();
|
||||
$order_items = DB::table('order_items')->where('channel_id', $channel->id)->count();
|
||||
$order_items_today = DB::table('order_items')->where('channel_id', $channel->id)->whereDate('created_at', Carbon::today())->count();
|
||||
|
||||
return response()->rest([
|
||||
'products_count' => $products_count,
|
||||
'order_items_count' => $order_items,
|
||||
'order_items_today_count' => $order_items_today,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Entrepreneur\Requests\VendorProductStoreRequest;
|
||||
use App\Http\Controllers\Api\V1\Entrepreneur\Requests\VendorProductUpdateRequest;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Review\ProductReviewResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\V1\Vendor\Product\VendorProductIndexResource;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use App\Models\User;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class VendorProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$channel = $user->channel();
|
||||
|
||||
if (! $channel) {
|
||||
return response()->rest([], 400, 'User doest not have a channel');
|
||||
}
|
||||
|
||||
return response()->rest_paginate(
|
||||
VendorProductIndexResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->queryAsFromResource($channel)
|
||||
->attachEagerLoadingRelationship([
|
||||
'brand',
|
||||
'media' => function ($query) {
|
||||
$query->orderBy('order_column', 'asc');
|
||||
},
|
||||
])
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(VendorProductStoreRequest $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$channel = $user->channel();
|
||||
$tax = productTaxForCategory($request->category_ids ?? []);
|
||||
|
||||
if ($request->brand_id == 0) {
|
||||
$request->merge(['brand_id' => null]);
|
||||
}
|
||||
|
||||
$this->addProduct($request, $user, $channel, $tax);
|
||||
|
||||
return response()->rest([], 201, 'Created');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add product
|
||||
*/
|
||||
public function addProduct(Request $request, User $user, mixed $channel, int $tax)
|
||||
{
|
||||
$product = Product::create([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'cost_amount' => $request->cost_amount,
|
||||
'price_amount' => ProductRepository::calculatePrice($request->cost_amount, $tax),
|
||||
'old_price_amount' => $request->old_price_amount ? ProductRepository::calculatePrice($request->old_price_amount, $tax) : null,
|
||||
'sku' => $request->sku,
|
||||
'barcode' => $request->barcode,
|
||||
'brand_id' => $request->brand_id,
|
||||
'stock' => $request->stock,
|
||||
'is_visible' => false,
|
||||
'options' => json_encode(ProductRepository::shippingAttributes()),
|
||||
]);
|
||||
|
||||
$product->categories()->attach($request->category_ids);
|
||||
$product->collections()->attach($request->collection_ids);
|
||||
$product->channels()->attach($channel->id);
|
||||
|
||||
$inventory = $channel->inventories()->first() ?: tmpostDefaultInventory();
|
||||
|
||||
$product->inventories()->attach($inventory->id, ['stock' => $request->stock]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
$product->addMultipleMediaFromRequest(['files'])
|
||||
->each(function ($fileAdder) {
|
||||
$fileAdder->preservingOriginal()->toMediaCollection('uploads');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param App\Models\Ecommerce\Product\Product\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Product $product)
|
||||
{
|
||||
$product->load('media');
|
||||
|
||||
return response()->rest(new VendorProductIndexResource($product));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(VendorProductUpdateRequest $request, Product $product)
|
||||
{
|
||||
$barcodeCheck = DB::table('products')
|
||||
->where('barcode', $request->barcode)
|
||||
->whereNot('id', $product->id)
|
||||
->exists();
|
||||
|
||||
if ($barcodeCheck) {
|
||||
return response()->rest([
|
||||
'message' => 'Used barcode',
|
||||
'errors' => ['barcode' => ['already taken']],
|
||||
]);
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
$channel = $user->channel();
|
||||
$tax = productTaxForCategory($request->category_ids ?? []);
|
||||
|
||||
$product->update([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'cost_amount' => $request->cost_amount,
|
||||
'price_amount' => ProductRepository::calculatePrice($request->cost_amount, $tax),
|
||||
'old_price_amount' => $request->old_price_amount
|
||||
? ProductRepository::calculatePrice($request->old_price_amount, $tax)
|
||||
: null,
|
||||
'sku' => $request->sku,
|
||||
'barcode' => $request->barcode,
|
||||
'brand_id' => $request->brand_id,
|
||||
'stock' => $request->stock,
|
||||
]);
|
||||
|
||||
$product->categories()->attach($request->category_ids);
|
||||
$product->collections()->attach($request->collection_ids);
|
||||
|
||||
$inventory = $channel->inventories()->first() ?: tmpostDefaultInventory();
|
||||
|
||||
$product->inventories()->sync($inventory->id, ['stock' => $request->stock]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
$product->addMultipleMediaFromRequest(['files'])
|
||||
->each(function ($fileAdder) {
|
||||
$fileAdder->preservingOriginal()->toMediaCollection('uploads');
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('deleted_images')) {
|
||||
$product->getMedia('uploads')->map(function ($media) use ($request) {
|
||||
if (in_array($media->getUrl('thumb'), $request->deleted_images)) {
|
||||
$media->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->hasFile('new_imgs')) {
|
||||
$product->addMultipleMediaFromRequest(['new_imgs'])
|
||||
->each(function ($fileAdder) {
|
||||
$fileAdder->toMediaCollection('uploads');
|
||||
});
|
||||
}
|
||||
|
||||
return response()->rest([], 201, 'Updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Product $product)
|
||||
{
|
||||
$product->delete();
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product reviews
|
||||
*/
|
||||
public function reviews(Product $product)
|
||||
{
|
||||
return ProductReviewResource::collection($product->reviews()->get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VendorProfileController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return response()->rest([
|
||||
'id' => $user->id,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'email' => $user->email,
|
||||
'phone_number' => $user->phone_number,
|
||||
'email_verified_at' => $user->email_verified_at,
|
||||
'verified' => $user->verified,
|
||||
'options' => $user->options,
|
||||
'created_at' => $user->created_at,
|
||||
'updated_at' => $user->updated_at,
|
||||
'phone_number_verified_at' => $user->phone_number_verified_at,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur\Resources\Order;
|
||||
|
||||
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
|
||||
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class OrderIndexResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'status' => OrderStatus::formattedStatusFor($this->status),
|
||||
'shipping_method' => OrderShipping::formattedShippingMethod($this->shipping_method),
|
||||
'notes' => $this->notes,
|
||||
'delivery_time' => $this->delivery_time,
|
||||
'delivery_at' => $this->delivery_at,
|
||||
'region' => $this->region,
|
||||
'payment_type' => $this->paymentType?->name,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Entrepreneur\Resources\Order;
|
||||
|
||||
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
|
||||
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class OrderShowResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'status' => OrderStatus::formattedStatusFor($this->status),
|
||||
'shipping_method' => OrderShipping::formattedShippingMethod($this->shipping_method),
|
||||
'notes' => $this->notes,
|
||||
'delivery_time' => $this->delivery_time,
|
||||
'delivery_at' => $this->delivery_at,
|
||||
'region' => $this->region,
|
||||
'payment_type' => $this->paymentType?->name,
|
||||
'products' => $this->items->map(fn ($item) => [
|
||||
'quantity' => $item->quantity,
|
||||
'product' => [
|
||||
'id' => $item->product->id,
|
||||
'name' => $item->product->name,
|
||||
'price_amount' => $item->product->price_amount,
|
||||
'thumbnail' => $item->product->thumbnail('thumb150x150'),
|
||||
],
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
42
app/Http/Controllers/Api/V1/Favorite/FavoriteController.php
Normal file
42
app/Http/Controllers/Api/V1/Favorite/FavoriteController.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Favorite;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Favorite\Requests\FavoriteStoreRequest;
|
||||
use App\Http\Controllers\Api\V1\Favorite\Resources\FavoriteResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class FavoriteController extends Controller
|
||||
{
|
||||
/**
|
||||
* (*) User's favorite products (index)
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
FavoriteResource::collection(
|
||||
auth()->user()->favorites()->with(['product' => ['media', 'brand']])->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Store resource
|
||||
*/
|
||||
public function store(FavoriteStoreRequest $request): JsonResponse
|
||||
{
|
||||
$data = auth()->user()->favorites()->where('product_id', $request->product_id)->first();
|
||||
|
||||
$status = $data
|
||||
? $data->delete()
|
||||
: auth()->user()->favorites()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'product_id' => $request->product_id,
|
||||
]);
|
||||
|
||||
return response()->rest(
|
||||
message: is_bool($status) ? 'Removed' : 'Added'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Favorite\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FavoriteStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'product_id' => ['required', 'integer', 'exists:products,id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Favorite\Resources;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class FavoriteResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'created_at' => $this->created_at,
|
||||
'product' => new ProductResource($this->whenLoaded('product')),
|
||||
];
|
||||
}
|
||||
}
|
||||
57
app/Http/Controllers/Api/V1/FilterParamsController.php
Normal file
57
app/Http/Controllers/Api/V1/FilterParamsController.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shop\Product\Attribute;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FilterParamsController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$category_id = (int) $request->category_id;
|
||||
|
||||
$attributes = Attribute::with('values:id,key,attribute_id,value')
|
||||
->where('is_enabled', true)
|
||||
->when($request->filled('category_id'), fn ($query) => $query->where('category_id', $category_id))
|
||||
->get(['id', 'slug', 'name', 'type', 'category_id', 'is_enabled']);
|
||||
|
||||
return [
|
||||
'brands' => $this->getBrands($request),
|
||||
'attributes' => $attributes->whereNotIn('slug', ['colour', 'size'])->flatten(1),
|
||||
'sizes' => $attributes->where('slug', 'size')->map(fn ($size_attribute) => [
|
||||
'attribute_id' => $size_attribute->id,
|
||||
'values' => $size_attribute->values,
|
||||
])->first(),
|
||||
'colours' => $attributes->where('slug', 'colour')->map(fn ($colour_attribute) => [
|
||||
'attribute_id' => $colour_attribute->id,
|
||||
'values' => $colour_attribute->values,
|
||||
])->first(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getBrands($request)
|
||||
{
|
||||
if ($request->filled('category_id')) {
|
||||
$category_id = (int) $request->category_id;
|
||||
|
||||
$brand_ids = DB::table('products')
|
||||
->join('product_has_relations', function ($join) use ($category_id) {
|
||||
$join->on('products.id', '=', 'product_has_relations.product_id')
|
||||
->where('product_has_relations.productable_id', '=', $category_id)
|
||||
->where('product_has_relations.productable_type', '=', 'category');
|
||||
})
|
||||
->select(['id', 'brand_id'])
|
||||
->pluck('brand_id');
|
||||
|
||||
return DB::table('brands')
|
||||
->where('is_enabled', true)
|
||||
->whereIn('id', $brand_ids)
|
||||
->get(['id', 'name']);
|
||||
}
|
||||
|
||||
return DB::table('brands')->where('is_enabled', true)->get(['id', 'name']);
|
||||
}
|
||||
}
|
||||
130
app/Http/Controllers/Api/V1/Filters/FilterController.php
Normal file
130
app/Http/Controllers/Api/V1/Filters/FilterController.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Filters;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Filters\Requests\FilterIndexRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Brand\Brand;
|
||||
use App\Models\Ecommerce\Product\Category\Category;
|
||||
use App\Models\Ecommerce\Product\Collection\Collection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FilterController extends Controller
|
||||
{
|
||||
/**
|
||||
* Filter Controller
|
||||
*/
|
||||
public function __construct(
|
||||
protected Request $request
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Filters
|
||||
*/
|
||||
public function index(FilterIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest([
|
||||
'categories' => $this->categories()->map(fn ($category) => [
|
||||
'id' => $category->id,
|
||||
'parent_id' => $category->parent_id,
|
||||
'name' => $category->name,
|
||||
]),
|
||||
'brands' => $this->brands(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories
|
||||
*/
|
||||
private function categories()
|
||||
{
|
||||
if ($this->shouldFilterByCategory()) {
|
||||
return Category::find($this->request->category_id, ['id', 'parent_id'])->children()->get(['id', 'parent_id', 'name']);
|
||||
}
|
||||
|
||||
if ($this->shouldFilterByCollection()) {
|
||||
return $this->filterByCategoryResource(Collection::find($this->request->collection_id));
|
||||
}
|
||||
|
||||
if ($this->shouldFilterByBrand()) {
|
||||
return $this->filterByCategoryResource(Brand::find($this->request->brand_id));
|
||||
}
|
||||
|
||||
return Category::query()->where('is_visible', true)->ordered()->get(['id', 'parent_id', 'name']);
|
||||
}
|
||||
|
||||
private function brands()
|
||||
{
|
||||
if ($this->shouldFilterByBrand()) {
|
||||
return Brand::where('id', $this->request->brand_id)->get(['id', 'name']);
|
||||
}
|
||||
|
||||
if ($this->shouldFilterByCategory()) {
|
||||
$brands = Category::find($this->request->category_id)->products()
|
||||
->where('products.is_visible', true)
|
||||
->where('products.parent_id', null)
|
||||
->where('products.stock', '>', 0)
|
||||
->distinct('products.brand_id')
|
||||
->pluck('products.brand_id');
|
||||
|
||||
return Brand::whereIntegerInRaw('id', $brands)->get(['id', 'name']);
|
||||
}
|
||||
|
||||
if ($this->shouldFilterByCollection()) {
|
||||
$brands = Collection::find($this->request->collection_id)->products()
|
||||
->where('products.is_visible', true)
|
||||
->where('products.parent_id', null)
|
||||
->where('products.stock', '>', 0)
|
||||
->distinct('products.brand_id')
|
||||
->pluck('products.brand_id');
|
||||
|
||||
return Brand::whereIntegerInRaw('id', $brands)->get(['id', 'name']);
|
||||
}
|
||||
|
||||
return Brand::query()->where('is_visible', true)->ordered()->get(['id', 'name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by category
|
||||
*/
|
||||
private function filterByCategoryResource($resource)
|
||||
{
|
||||
$products = $resource->products()
|
||||
->where('products.is_visible', true)
|
||||
->where('products.parent_id', null)
|
||||
->where('products.stock', '>', 0)
|
||||
->distinct('products.id')
|
||||
->pluck('products.id');
|
||||
|
||||
return Category::where('is_visible', true)->ordered()->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
|
||||
->where('product_has_relations.productable_type', '=', 'category')
|
||||
->whereIntegerInRaw('product_has_relations.product_id', $products)
|
||||
->get(['id', 'parent_id', 'name'])
|
||||
->unique('categories.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if request should be filtered by category
|
||||
*/
|
||||
private function shouldFilterByCategory(): bool
|
||||
{
|
||||
return $this->request->filled('category_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if request should be filtered by collection
|
||||
*/
|
||||
private function shouldFilterByCollection(): bool
|
||||
{
|
||||
return $this->request->filled('collection_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if request should be filtered by brand
|
||||
*/
|
||||
private function shouldFilterByBrand(): bool
|
||||
{
|
||||
return $this->request->filled('brand_id');
|
||||
}
|
||||
}
|
||||
38
app/Http/Controllers/Api/V1/Filters/Helpers/BrandsFilter.php
Normal file
38
app/Http/Controllers/Api/V1/Filters/Helpers/BrandsFilter.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Filters\Helpers;
|
||||
|
||||
use Laravel\Nova\Makeable;
|
||||
|
||||
class BrandsFilter
|
||||
{
|
||||
use Makeable;
|
||||
|
||||
public function __construct(
|
||||
protected Builder $queryBuilder,
|
||||
protected Request $request,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Basic queries
|
||||
*/
|
||||
public function applyBaseQueries(): self
|
||||
{
|
||||
$this->queryBuilder->where('is_visible', true)->ordered();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get brands
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->queryBuilder->whereIntegerInRaw(
|
||||
column: 'id',
|
||||
values: $resource->products()->distinct('brand_id')->pluck('brand_id')
|
||||
)->get(['id', 'name']);
|
||||
|
||||
return $this->queryBuilder->get(['id', 'name']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Filters\Helpers;
|
||||
|
||||
use App\Models\Ecommerce\Product\Brand\Brand;
|
||||
use App\Models\Ecommerce\Product\Category\Category;
|
||||
use App\Models\Ecommerce\Product\Collection\Collection;
|
||||
use App\Repositories\Ecommerce\Filter\FilterParamRepository;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Nova\Makeable;
|
||||
|
||||
class CategoriesFilter
|
||||
{
|
||||
use Makeable;
|
||||
|
||||
/**
|
||||
* Request object to work with
|
||||
*/
|
||||
protected Request $request;
|
||||
|
||||
/**
|
||||
* Request object to work with
|
||||
*/
|
||||
protected Builder $queryBuilder;
|
||||
|
||||
/**
|
||||
* Filter repo
|
||||
*/
|
||||
protected FilterParamRepository $filterRepository;
|
||||
|
||||
/**
|
||||
* Table columns to get
|
||||
*/
|
||||
protected array $columns = [
|
||||
'id',
|
||||
'parent_id',
|
||||
'name',
|
||||
];
|
||||
|
||||
/**
|
||||
* Categories for filtering
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->queryBuilder = config('ecommerce.models.category')::query();
|
||||
$this->filterRepository = FilterParamRepository::make($this->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic queries
|
||||
*/
|
||||
public function applyBaseQueries(): self
|
||||
{
|
||||
$this->queryBuilder->enabled()->ordered();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all categories
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
if ($this->filterRepository->shouldFilterByCategory()) {
|
||||
return Category::find($this->request->category_id, ['id', 'parent_id'])->children()->get($this->columns);
|
||||
}
|
||||
|
||||
if ($this->filterRepository->shouldFilterByCollection()) {
|
||||
return $this->byResource(Collection::find($this->request->collection_id));
|
||||
}
|
||||
|
||||
if ($this->filterRepository->shouldFilterByBrand()) {
|
||||
return $this->byResource(Brand::find($this->request->brand_id));
|
||||
}
|
||||
|
||||
return $this->queryBuilder->get($this->columns);
|
||||
}
|
||||
|
||||
public function byResource($resource)
|
||||
{
|
||||
$products = $resource->products()
|
||||
->where('products.is_visible', true)
|
||||
->where('products.parent_id', null)
|
||||
->where('products.stock', '>', 0)
|
||||
->distinct('products.id')
|
||||
->pluck('products.id');
|
||||
|
||||
return $this->queryBuilder->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
|
||||
->where('product_has_relations.productable_type', '=', 'category')
|
||||
->whereIntegerInRaw('product_has_relations.product_id', $products)
|
||||
->get($this->columns)
|
||||
->unique('categories.id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Filters\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FilterIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'collection_id' => ['bail', 'nullable', 'int', 'exists:collections,id'],
|
||||
'category_id' => ['bail', 'nullable', 'int', 'exists:categories,id'],
|
||||
'brand_id' => ['bail', 'nullable', 'int', 'exists:brands,id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
34
app/Http/Controllers/Api/V1/Legal/LegalPageController.php
Normal file
34
app/Http/Controllers/Api/V1/Legal/LegalPageController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Legal;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\V1\Legal\LegalPageResource;
|
||||
use App\Models\Legal\LegalPage;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class LegalPageController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Legal) Legal pages
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
LegalPage::where('is_active', true)
|
||||
->get(['slug', 'title'])
|
||||
->map(fn ($legalPage) => [
|
||||
'slug' => $legalPage->slug,
|
||||
'title' => $legalPage->title,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Legal) Legal pages
|
||||
*/
|
||||
public function show(LegalPage $legalPage): JsonResponse
|
||||
{
|
||||
return response()->rest(new LegalPageResource($legalPage));
|
||||
}
|
||||
}
|
||||
42
app/Http/Controllers/Api/V1/LegalPagesController.php
Normal file
42
app/Http/Controllers/Api/V1/LegalPagesController.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Legal\LegalPage;
|
||||
|
||||
class LegalPagesController extends Controller
|
||||
{
|
||||
public function refundPolicy()
|
||||
{
|
||||
return response()->rest(
|
||||
LegalPage::where('slug', 'refund-policy')->first(['title', 'content'])
|
||||
);
|
||||
}
|
||||
|
||||
public function privacyPolicy()
|
||||
{
|
||||
return response()->rest(
|
||||
LegalPage::where('slug', 'privacy-policy')->first(['title', 'content'])
|
||||
);
|
||||
}
|
||||
|
||||
public function termsOfUse()
|
||||
{
|
||||
return response()->rest(
|
||||
LegalPage::where('slug', 'terms-of-use')->first(['title', 'content'])
|
||||
);
|
||||
}
|
||||
|
||||
public function shippingPolicy()
|
||||
{
|
||||
return response()->rest(
|
||||
LegalPage::where('slug', 'shipping-policy')->first(['title', 'content'])
|
||||
);
|
||||
}
|
||||
|
||||
public function paymentTypeNotAvailable()
|
||||
{
|
||||
return 'Töleg görnüşi elýeter däl';
|
||||
}
|
||||
}
|
||||
69
app/Http/Controllers/Api/V1/LikeController.php
Normal file
69
app/Http/Controllers/Api/V1/LikeController.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Products\ProductResource;
|
||||
use App\Models\Shop\Product\Product;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Maize\Markable\Models\Like;
|
||||
|
||||
class LikeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return response()->rest_paginate(ProductResource::collection(
|
||||
Product::with(['media', 'brand'])->whereHasLike(auth()->user())->paginate(15)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'product_id' => ['required', 'integer', 'exists:products,id'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$product = Product::find($request->product_id);
|
||||
|
||||
Like::add($product, auth()->user());
|
||||
|
||||
return response()->rest([], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'product_id' => ['required', 'integer', 'exists:products,id'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest([], 400, 'Wrong credentials');
|
||||
}
|
||||
|
||||
$product = Product::find($request->product_id);
|
||||
Like::remove($product, auth()->user());
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/Api/V1/LinkController.php
Normal file
30
app/Http/Controllers/Api/V1/LinkController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class LinkController extends Controller
|
||||
{
|
||||
public function product(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'id' => ['required', 'integer'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->rest($validator->messages()->get('*'), 400, 'Validator fail');
|
||||
}
|
||||
|
||||
$product = DB::table('products')->where('id', $request->id)->pluck('slug', 'id');
|
||||
|
||||
if ($product->isEmpty()) {
|
||||
return to_route('web.products.show', ['product' => 1]);
|
||||
}
|
||||
|
||||
return to_route('web.products.show', ['product' => $product->first()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Forms\Newsletter\NewsletterSubscriptionStoreRequest;
|
||||
use App\Models\CMS\Marketing\NewsletterUser;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class NewsletterSubscriptionController extends Controller
|
||||
{
|
||||
/**
|
||||
* (Marketing) Subscribe to newsletter
|
||||
*/
|
||||
public function store(NewsletterSubscriptionStoreRequest $request): JsonResponse
|
||||
{
|
||||
NewsletterUser::insertOrIgnore($request->validated());
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
132
app/Http/Controllers/Api/V1/OnlinePaymentController.php
Normal file
132
app/Http/Controllers/Api/V1/OnlinePaymentController.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Api\Services\Payment\HalkbankPaymentService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shop\Order\Order;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class OnlinePaymentController extends Controller
|
||||
{
|
||||
public function halkbank(Request $request)
|
||||
{
|
||||
$formData = $request->validate([
|
||||
'pan' => ['required', 'integer', 'digits:16'],
|
||||
'month' => ['required', 'integer', 'between:1,12'],
|
||||
'year' => ['required', 'integer', 'min:2022', 'max:2066'],
|
||||
'name' => ['required', 'string'],
|
||||
'cvc' => ['required'],
|
||||
'order_id' => ['required', 'integer', 'exists:orders,id'],
|
||||
]);
|
||||
|
||||
return route('payment-not-available');
|
||||
|
||||
$order = Order::find($request->order_id);
|
||||
|
||||
$halkbank = new HalkbankPaymentService(
|
||||
amount: (string) $order->fullPriceWithShipping().'00',
|
||||
returnURL: route('web.order.check.halkbank', ['id' => $order->id])
|
||||
);
|
||||
|
||||
$halkbank->orderTicket();
|
||||
|
||||
$app_name = 'POSTSHOP';
|
||||
$app_id = Str::random(16);
|
||||
|
||||
// Wagtyny kesgitlemek
|
||||
$response_start_hack = Http::asForm()->post('http://localhost:9090/api/v1/start-hack', [
|
||||
'app' => $app_name,
|
||||
'id' => $app_id,
|
||||
'url' => $halkbank->paymentPageUrl(),
|
||||
]);
|
||||
|
||||
Log::info(['hack' => $response_start_hack->json()]);
|
||||
|
||||
if (! ($response_start_hack && array_key_exists('status', $response_start_hack->json()) && $response_start_hack['status'] == 'ok')) {
|
||||
return response()->rest([$response_start_hack->body()]);
|
||||
}
|
||||
|
||||
$ok = (string) $request->cvc;
|
||||
|
||||
$cvc = match (strlen($ok)) {
|
||||
1 => '00'.$ok,
|
||||
2 => '0'.$ok,
|
||||
default => $ok
|
||||
};
|
||||
|
||||
$response_submit_card = Http::asForm()->post('http://localhost:9090/api/v1/submit-card', [
|
||||
'app' => $app_name,
|
||||
'id' => $app_id,
|
||||
'md-order' => $halkbank->ticketOrderId(),
|
||||
'card-number' => $request->pan,
|
||||
'card-expiry' => $request->year.$request->month,
|
||||
'name-on-card' => $request->name,
|
||||
'card-cvc' => $cvc,
|
||||
]);
|
||||
|
||||
Log::info(['submit_card' => $response_submit_card->json()]);
|
||||
|
||||
if (! ($response_submit_card && array_key_exists('status', $response_submit_card->json()) && $response_submit_card['status'] == 'ok')) {
|
||||
return response()->rest([$response_submit_card->body()]);
|
||||
}
|
||||
|
||||
return response()->rest([
|
||||
'order_id' => $order->id,
|
||||
'ticket_order_id' => $halkbank->ticketOrderId(),
|
||||
'url' => $response_submit_card['acs-session-url'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkPayment(Request $request)
|
||||
{
|
||||
$request->validate(['order_id' => 'required|integer|exists:orders,id', 'ticket_order_id' => 'required|string']);
|
||||
|
||||
$payment_status = HalkbankPaymentService::checkPayment($request->ticket_order_id);
|
||||
if ($payment_status) {
|
||||
Order::find($request->order_id)->markAsPaid();
|
||||
}
|
||||
|
||||
return response()->rest([
|
||||
'payment_status' => $payment_status,
|
||||
]);
|
||||
}
|
||||
|
||||
public function halkbankVerifyOTP(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'order_id' => ['required', 'integer', 'exists:orders,id'],
|
||||
'request_id' => ['required'],
|
||||
'sms_code' => ['required', 'integer'],
|
||||
]);
|
||||
|
||||
$response = HalkbankPaymentService::sendSMSVerificationCode(
|
||||
request_id: $request->request_id,
|
||||
sms_code: $request->sms_code
|
||||
);
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML($response->body());
|
||||
|
||||
$inputs = $doc->getElementsByTagName('input');
|
||||
|
||||
if (count($inputs) > 0) {
|
||||
$md = $inputs[0]->getAttribute('value');
|
||||
$paRes = $inputs[1]->getAttribute('value');
|
||||
}
|
||||
|
||||
if ($md && $paRes) {
|
||||
$response_3d = Http::asForm()->post('https://mpi.gov.tm:443/payment/rest/finish3ds.do', [
|
||||
'MD' => $md,
|
||||
'PaRes' => $paRes,
|
||||
]);
|
||||
|
||||
Log::info('3d', [$response_3d->body()]);
|
||||
}
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
24
app/Http/Controllers/Api/V1/Order/OrderPaymentController.php
Normal file
24
app/Http/Controllers/Api/V1/Order/OrderPaymentController.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Order;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\System\Settings\Payments\PaymentType;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class OrderPaymentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Order payment types
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
PaymentType::all(['id', 'name'])
|
||||
->map(fn ($paymentType) => [
|
||||
'id' => $paymentType->id,
|
||||
'name' => $paymentType->name,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
90
app/Http/Controllers/Api/V1/OrderController.php
Normal file
90
app/Http/Controllers/Api/V1/OrderController.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\CheckoutOrderRequest;
|
||||
use App\Http\Resources\Api\V1\Order\OrderIndexResource;
|
||||
use App\Models\Ecommerce\Product\Order\Order;
|
||||
use App\Repositories\Ecommerce\Order\OrderRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
/**
|
||||
* (*) User's orders
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
OrderIndexResource::collection(
|
||||
auth()->user()->orders()->with(['items', 'items.product', 'paymentType'])->latest()->paginate(20)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Return available times to make an order
|
||||
*/
|
||||
public function time(): JsonResponse
|
||||
{
|
||||
return response()->rest(OrderRepository::availableTimes());
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Store order
|
||||
*/
|
||||
public function store(CheckoutOrderRequest $request): JsonResponse
|
||||
{
|
||||
$order = (new OrderRepository($request->all()))->create();
|
||||
|
||||
$url = null;
|
||||
if ($request->payment_type_id == 3) {
|
||||
$response = createHalkbankOrder();
|
||||
|
||||
if ($response['status'] == 'success') {
|
||||
$url = $response['url'];
|
||||
}
|
||||
}
|
||||
|
||||
return response()->rest([
|
||||
'order_id' => $order->id,
|
||||
'payment_url' => $url
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Show order
|
||||
*/
|
||||
public function show(Order $order): JsonResponse
|
||||
{
|
||||
$order->load(['items', 'items.product', 'paymentType']);
|
||||
|
||||
return response()->rest(new OrderIndexResource($order));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, Order $order): JsonResponse
|
||||
{
|
||||
// $order-.
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\PostBranch;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Post\PostBranch;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PostBranchController extends Controller
|
||||
{
|
||||
/**
|
||||
* All post branches
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
PostBranch::query()
|
||||
->get()
|
||||
->map(fn ($postBranch) => [
|
||||
'id' => $postBranch->id,
|
||||
'province_id' => $postBranch->province_id,
|
||||
'name' => $postBranch->name,
|
||||
'address' => $postBranch->address,
|
||||
'description' => $postBranch->description,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Barcode;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Barcode\Requests\ProductBarcodeSearchIndexRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ProductBarcodeSearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* Search product via barcode
|
||||
*/
|
||||
public function index(ProductBarcodeSearchIndexRequest $request): JsonResponse
|
||||
{
|
||||
$product = Product::where('barcode', $request->barcode)->with('media')->first();
|
||||
|
||||
return response()->rest([
|
||||
'id' => $product->id,
|
||||
'name' => $product->name,
|
||||
'stock' => $product->stock,
|
||||
'price_amount' => $product->price_amount,
|
||||
'image' => [
|
||||
'thumbnail' => $product->thumbnail(),
|
||||
'images_400x400' => $product->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb400x400')),
|
||||
'images_720x720' => $product->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb720x720')),
|
||||
'images_800x800' => $product->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb800x800')),
|
||||
'images_1200x1200' => $product->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb1200x1200')),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Barcode\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ProductBarcodeSearchIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'barcode' => ['required', 'string', 'max:255', 'exists:products,barcode'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Product\ProductCommentStore;
|
||||
use App\Http\Resources\Api\V1\Common\CommentResource;
|
||||
use App\Models\Common\Comment;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductCommentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Product's comments
|
||||
*/
|
||||
public function index(Request $request, Product $product): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
CommentResource::collection(
|
||||
$product->comments()->with('user')->where('active', true)->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store
|
||||
*/
|
||||
public function store(ProductCommentStore $request, Product $product): JsonResponse
|
||||
{
|
||||
$product->comments()->create([
|
||||
'comment' => $request->comment,
|
||||
'user_id' => auth()->id(),
|
||||
]);
|
||||
|
||||
return response()->rest(message: 'Comment added successfully', code: 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the comment from product
|
||||
*/
|
||||
public function destroy(Product $product, Comment $comment): JsonResponse
|
||||
{
|
||||
$product->comments()->where('comments.id', $comment->id)->delete();
|
||||
|
||||
return response()->rest(message: 'Comment removed successfully from product', code: 204);
|
||||
}
|
||||
}
|
||||
52
app/Http/Controllers/Api/V1/Product/ProductController.php
Normal file
52
app/Http/Controllers/Api/V1/Product/ProductController.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductIndexResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductShowResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Product\ProductIndexRequest;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* Products (index)
|
||||
*/
|
||||
public function index(ProductIndexRequest $request): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ProductIndexResource::collection(
|
||||
ProductRepository::make($request)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Products (show)
|
||||
*/
|
||||
public function show(Product $product): JsonResponse
|
||||
{
|
||||
$product->load([
|
||||
'channels:id,name',
|
||||
'properties',
|
||||
'media' => function ($query) {
|
||||
$query->orderBy('order_column', 'asc');
|
||||
},
|
||||
'variations' => [
|
||||
'media',
|
||||
'properties',
|
||||
],
|
||||
'reviews',
|
||||
'categories:id,name',
|
||||
]);
|
||||
|
||||
return response()->rest(new ProductShowResource($product));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ProductRelatedController extends Controller
|
||||
{
|
||||
/**
|
||||
* Related products (index)
|
||||
*/
|
||||
public function index(Product $product): JsonResponse
|
||||
{
|
||||
$products = DB::table('product_has_relations')
|
||||
->select('product_has_relations.product_id')
|
||||
->whereIn('product_has_relations.productable_id', (function ($query) use ($product) {
|
||||
$query->from('product_has_relations')
|
||||
->select('productable_id')
|
||||
->distinct('productable_id')
|
||||
->where('productable_type', '=', 'category')
|
||||
->where('product_id', '=', $product->id);
|
||||
}))
|
||||
->limit(12)
|
||||
->orderByRaw('RANDOM()')
|
||||
->pluck('product_has_relations.product_id')
|
||||
->unique();
|
||||
|
||||
return response()->rest(
|
||||
ProductResource::collection(
|
||||
ProductRepository::make(request())
|
||||
->applyBasicQueries()
|
||||
->whereIntegerInRaw('id', $products->toArray())
|
||||
->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Review\ProductReviewResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Product\Review\ProductReviewStore;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ProductReviewController extends Controller
|
||||
{
|
||||
/**
|
||||
* Product reviews (index)
|
||||
*/
|
||||
public function index(Product $product): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
ProductReviewResource::collection(
|
||||
$product->reviews()
|
||||
->where('is_visible', true)
|
||||
->orderBy('is_recommended')
|
||||
->get()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Product reviews (store)
|
||||
*/
|
||||
public function store(ProductReviewStore $request, Product $product): JsonResponse
|
||||
{
|
||||
$product->reviews()->create([
|
||||
'user_id' => auth()->id(),
|
||||
...$request->validated(),
|
||||
]);
|
||||
|
||||
return response()->rest(message: 'Review added successfully', code: 201);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources\Attribute;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductAttributeProductShowResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'attribute_id' => $this->attribute->id,
|
||||
'name' => $this->attribute->name,
|
||||
'value' => $this->values->map(fn ($val) => $val->real_value)->first(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources\Attribute;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductAttributeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'attribute_id' => $this->attribute->id,
|
||||
'name' => $this->attribute->name,
|
||||
'value' => $this->values->map(fn ($val) => $val->real_value)->first(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources;
|
||||
|
||||
use App\Http\Resources\MediaResource;
|
||||
use App\Repositories\Ecommerce\Product\Property\PropertyRepository;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductIndexResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
$reviews = $this->whenLoaded('reviews');
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'parent_id' => $this->parent_id,
|
||||
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'description' => '',
|
||||
|
||||
'sku' => $this->sku,
|
||||
'barcode' => $this->barcode,
|
||||
'stock' => $this->stock,
|
||||
|
||||
'price_amount' => $this->price_amount,
|
||||
'old_price_amount' => $this->old_price_amount,
|
||||
|
||||
'backorder' => $this->backorder,
|
||||
'weight_value' => $this->weight_value,
|
||||
'weight_unit' => $this->weight_unit,
|
||||
'height_value' => $this->height_value,
|
||||
'height_unit' => $this->height_unit,
|
||||
|
||||
'media' => MediaResource::collection($this->whenLoaded('media')),
|
||||
|
||||
'created_at' => $this->created_at,
|
||||
|
||||
'seo_title' => $this->seo_title,
|
||||
'seo_description' => $this->seo_description,
|
||||
|
||||
'colour' => PropertyRepository::getRealValue('colour', $this->colour),
|
||||
'size' => PropertyRepository::getRealValue('size', $this->size),
|
||||
|
||||
'brand' => [
|
||||
'id' => $this->brand_id,
|
||||
'name' => $this->brand?->name,
|
||||
],
|
||||
|
||||
'reviews' => [
|
||||
'count' => $reviews ? $reviews->count() : 0,
|
||||
'rating' => $reviews ? number_format($reviews->avg('rating'), 2) : 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductMediaResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'thumbnail' => $this->getUrl('thumb288x431'),
|
||||
'images_400x400' => $this->getUrl('thumb400x400'),
|
||||
'images_720x720' => $this->getUrl('thumb720x720'),
|
||||
'images_800x800' => $this->getUrl('thumb800x800'),
|
||||
'images_1200x1200' => $this->getUrl('thumb1200x1200'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Attribute\ProductAttributeProductShowResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Variant\ProductVariantResource;
|
||||
use App\Http\Resources\Api\V1\Channel\ChannelResource;
|
||||
use App\Repositories\Ecommerce\Product\Property\PropertyRepository;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
$media = $this->whenLoaded('media');
|
||||
$reviews = $this->whenLoaded('reviews');
|
||||
|
||||
$reviewsLoaded = get_class($reviews) !== 'Illuminate\\Http\\Resources\\MissingValue';
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'parent_id' => $this->parent_id,
|
||||
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'description' => $this->description,
|
||||
|
||||
'sku' => $this->sku,
|
||||
'barcode' => $this->barcode,
|
||||
'stock' => $this->stock,
|
||||
|
||||
'price_amount' => $this->price_amount,
|
||||
'old_price_amount' => $this->old_price_amount,
|
||||
|
||||
'backorder' => $this->backorder,
|
||||
'weight_value' => $this->weight_value,
|
||||
'weight_unit' => $this->weight_unit,
|
||||
'height_value' => $this->height_value,
|
||||
'height_unit' => $this->height_unit,
|
||||
|
||||
'media' => ProductMediaResource::collection($media),
|
||||
|
||||
'created_at' => $this->created_at,
|
||||
|
||||
'seo_title' => $this->seo_title,
|
||||
'seo_description' => $this->seo_description,
|
||||
|
||||
'is_visible' => $this->is_visible,
|
||||
|
||||
'colour' => PropertyRepository::getRealValue('colour', $this->colour),
|
||||
'size' => PropertyRepository::getRealValue('size', $this->size),
|
||||
|
||||
'brand' => [
|
||||
'id' => $this->brand_id,
|
||||
'name' => $this->brand?->name,
|
||||
],
|
||||
|
||||
'channel' => ChannelResource::collection($this->whenLoaded('channels')),
|
||||
|
||||
'properties' => ProductAttributeProductShowResource::collection($this->whenLoaded('properties')),
|
||||
'variations' => ProductVariantResource::collection($this->whenLoaded('variations')),
|
||||
|
||||
'reviews' => [
|
||||
'count' => $reviewsLoaded ? $reviews->count() : 0,
|
||||
'rating' => $reviewsLoaded ? number_format($reviews->avg('rating'), 2) : '',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Attribute\ProductAttributeProductShowResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Review\ProductReviewResource;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Variant\ProductVariantResource;
|
||||
use App\Http\Resources\Api\V1\Channel\ChannelResource;
|
||||
use App\Http\Resources\MediaResource;
|
||||
use App\Repositories\Ecommerce\Product\Property\PropertyRepository;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductShowResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
$reviews = $this->whenLoaded('reviews');
|
||||
$categories = $this->whenLoaded('categories');
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'parent_id' => $this->parent_id,
|
||||
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'description' => $this->description,
|
||||
|
||||
'sku' => $this->sku,
|
||||
'barcode' => $this->barcode,
|
||||
'stock' => $this->stock,
|
||||
|
||||
'price_amount' => $this->price_amount,
|
||||
'old_price_amount' => $this->old_price_amount,
|
||||
|
||||
'backorder' => $this->backorder ? 'true' : 'false',
|
||||
'weight_value' => $this->weight_value,
|
||||
'weight_unit' => $this->weight_unit,
|
||||
'height_value' => $this->height_value,
|
||||
'height_unit' => $this->height_unit,
|
||||
|
||||
'media' => MediaResource::collection($this->whenLoaded('media')),
|
||||
|
||||
'created_at' => $this->created_at,
|
||||
|
||||
'seo_title' => $this->seo_title,
|
||||
'seo_description' => $this->seo_description,
|
||||
|
||||
'colour' => PropertyRepository::getRealValue('colour', $this->colour),
|
||||
'size' => PropertyRepository::getRealValue('size', $this->size),
|
||||
|
||||
'available_colors' => PropertyRepository::getAvailableColors($this),
|
||||
'available_sizes' => PropertyRepository::getAvailableSizes($this),
|
||||
|
||||
'brand' => [
|
||||
'id' => $this->brand_id,
|
||||
'name' => $this->brand?->name,
|
||||
],
|
||||
|
||||
'channel' => ChannelResource::collection($this->whenLoaded('channels')),
|
||||
|
||||
'properties' => ProductAttributeProductShowResource::collection($this->whenLoaded('properties')),
|
||||
'variations' => ProductVariantResource::collection($this->whenLoaded('variations')),
|
||||
'reviews' => [
|
||||
'count' => $reviews ? $reviews->count() : 0,
|
||||
'rating' => $reviews ? number_format($reviews->avg('rating'), 2) : 0,
|
||||
],
|
||||
|
||||
'reviews_resources' => ProductReviewResource::collection($this->whenLoaded('reviews')),
|
||||
|
||||
$this->mergeWhen($categories, [
|
||||
'categories' => $categories->map(fn ($category) => [
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
]),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources\Review;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductReviewResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'user_id' => $this->user_id,
|
||||
'product_id' => $this->product_id,
|
||||
'rating' => $this->rating,
|
||||
'title' => $this->title,
|
||||
'content' => $this->content,
|
||||
'is_recommended' => $this->is_recommended,
|
||||
'source' => $this->source,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Resources\Variant;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Attribute\ProductAttributeResource;
|
||||
use App\Repositories\Ecommerce\Product\Property\PropertyRepository;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductVariantResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'parent_id' => $this->parent_id,
|
||||
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'description' => $this->description,
|
||||
|
||||
'sku' => $this->sku,
|
||||
'barcode' => $this->barcode,
|
||||
'stock' => $this->stock,
|
||||
|
||||
'cost_amount' => $this->cost_amount,
|
||||
'price_amount' => $this->price_amount,
|
||||
'old_price_amount' => $this->old_price_amount,
|
||||
|
||||
'backorder' => $this->backorder,
|
||||
'weight_value' => $this->weight_value,
|
||||
'weight_unit' => $this->weight_unit,
|
||||
'height_value' => $this->height_value,
|
||||
'height_unit' => $this->height_unit,
|
||||
|
||||
'colour' => PropertyRepository::getRealValue('colour', $this->colour),
|
||||
'size' => PropertyRepository::getRealValue('size', $this->size),
|
||||
|
||||
'thumbnail' => $this->thumbnail(),
|
||||
'images_400x400' => $this->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb400x400')),
|
||||
'images_800x800' => $this->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb800x800')),
|
||||
'images_1200x1200' => $this->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb1200x1200')),
|
||||
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
|
||||
'properties' => ProductAttributeResource::collection($this->whenLoaded('properties')),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Product\Search;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\ProductMediaResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductSearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* Search product via barcode
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'q' => ['required', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
$searchQuery = $request->input('q');
|
||||
|
||||
$products = Product::with(['brand', 'media'])
|
||||
->where(function ($query) use ($searchQuery) {
|
||||
if (is_numeric($searchQuery)) {
|
||||
$query->where('products.id', intval($searchQuery));
|
||||
}
|
||||
|
||||
$query
|
||||
->orWhere('products.name', '~*', $searchQuery)
|
||||
->orWhere('products.sku', '~*', $searchQuery)
|
||||
->orWhere('products.barcode', '~*', $searchQuery);
|
||||
})
|
||||
->when($request->isNotFilled('internal'), function ($query) {
|
||||
$query->where('products.is_visible', true)
|
||||
->where('products.parent_id', null)
|
||||
->where('products.stock', '>', 0);
|
||||
})
|
||||
->limit(40)
|
||||
->get();
|
||||
|
||||
return response()->rest(
|
||||
$products->map(fn ($product) => [
|
||||
'id' => $product->id,
|
||||
'name' => $product->fullName,
|
||||
'stock' => $product->stock,
|
||||
'cost_amount' => $product->cost_amount,
|
||||
'price_amount' => $product->price_amount,
|
||||
'brand' => [
|
||||
'id' => $product->brand?->id,
|
||||
'name' => $product->brand?->name,
|
||||
],
|
||||
'thumbnail' => $product->thumbnail(),
|
||||
'media' => ProductMediaResource::collection($product->media),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
47
app/Http/Controllers/Api/V1/Profile/ProfileController.php
Normal file
47
app/Http/Controllers/Api/V1/Profile/ProfileController.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Profile;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Users profile
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return response()->rest([
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'phone_number' => $user->phone_number,
|
||||
'address' => $user->options->get('address'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'phone_number' => ['required', 'int', 'between:61000000,71999999', Rule::unique('users', 'phone_number')->ignore(auth()->id())],
|
||||
'address' => ['required', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
auth()->user()->update([
|
||||
'first_name' => $request->name,
|
||||
'phone_number' => $request->phone_number,
|
||||
]);
|
||||
|
||||
auth()->user()->options->set('address', $request->address);
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
26
app/Http/Controllers/Api/V1/Province/ProvinceController.php
Normal file
26
app/Http/Controllers/Api/V1/Province/ProvinceController.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Province;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\System\Settings\Location\Province;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProvinceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get provinces
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
return response()->rest(
|
||||
Province::query()
|
||||
->get()
|
||||
->map(fn ($province) => [
|
||||
'id' => $province->id,
|
||||
'region' => $province->region,
|
||||
'name' => $province->name,
|
||||
]));
|
||||
}
|
||||
}
|
||||
45
app/Http/Controllers/Api/V1/ReviewController.php
Normal file
45
app/Http/Controllers/Api/V1/ReviewController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Api\V1\Reviews\Resources\ReviewResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\Product\Review\ProductReviewUpdate;
|
||||
use App\Models\Ecommerce\Product\Review\Review;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ReviewController extends Controller
|
||||
{
|
||||
/**
|
||||
* (*) User's reviews
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
return response()->rest_paginate(
|
||||
ReviewResource::collection(
|
||||
auth()->user()->reviews()->with(['product', 'product.brand'])->simplePaginate($request->perPage ?: 10)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Update review
|
||||
*/
|
||||
public function update(ProductReviewUpdate $request, Review $review): JsonResponse
|
||||
{
|
||||
$review->update($request->validated());
|
||||
|
||||
return response()->rest(message: 'Review updated successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* (*) Destroy the comment from product
|
||||
*/
|
||||
public function destroy(Review $review): JsonResponse
|
||||
{
|
||||
$review->delete();
|
||||
|
||||
return response()->rest(message: 'Review deleted successfully');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Reviews\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ReviewResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'product' => $this->when($this->relationLoaded('product'), [
|
||||
'id' => $this->product->id,
|
||||
'name' => $this->product->name,
|
||||
'barcode' => $this->product->barcode,
|
||||
'price_amount' => $this->product->price_amount,
|
||||
'brand' => [
|
||||
'id' => $this->product->brand_id,
|
||||
'name' => $this->product->brand?->name,
|
||||
],
|
||||
]),
|
||||
'rating' => $this->rating,
|
||||
'title' => $this->title,
|
||||
'content' => $this->content,
|
||||
'is_recommended' => $this->is_recommended,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
32
app/Http/Controllers/BarcodeGeneratorController.php
Normal file
32
app/Http/Controllers/BarcodeGeneratorController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Milon\Barcode\Facades\DNS1DFacade;
|
||||
|
||||
class BarcodeGeneratorController extends Controller
|
||||
{
|
||||
/**
|
||||
* Barcode image generator
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'barcode' => ['required'],
|
||||
]);
|
||||
|
||||
return Storage::download(
|
||||
Str::after(
|
||||
DNS1DFacade::getBarcodePNGPath(
|
||||
code: $request->barcode,
|
||||
type: config('barcode.default_type'),
|
||||
showCode: true,
|
||||
),
|
||||
'storage/app/'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
12
app/Http/Controllers/Controller.php
Normal file
12
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
}
|
||||
96
app/Http/Controllers/TestController.php
Normal file
96
app/Http/Controllers/TestController.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\CMS\Forms\ContactUS;
|
||||
use App\Models\CMS\Marketing\Newsletter;
|
||||
use App\Models\CMS\Marketing\NewsletterUser;
|
||||
use App\Models\CMS\Media\Banner;
|
||||
use App\Models\CMS\Media\Carousel;
|
||||
use App\Models\CMS\Media\Gallery;
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Models\Ecommerce\Product\Brand\Brand;
|
||||
use App\Models\Ecommerce\Product\Category\Category;
|
||||
use App\Models\Ecommerce\Product\Collection\Collection;
|
||||
use App\Models\Ecommerce\Product\Coupon\Coupon;
|
||||
use App\Models\Ecommerce\Product\Inventory\Inventory;
|
||||
use App\Models\Ecommerce\Product\Order\Order;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use App\Models\Ecommerce\Product\Property\Attribute;
|
||||
use App\Models\Ecommerce\Product\Review\Review;
|
||||
use App\Models\Legal\LegalPage;
|
||||
use App\Models\Post\PostBranch;
|
||||
use App\Models\System\Roles\Permission;
|
||||
use App\Models\System\Roles\Role;
|
||||
use App\Models\System\Settings\Location\Province;
|
||||
use App\Models\System\Settings\Payments\PaymentType;
|
||||
use App\Models\System\VersionManagement\AppVersion;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class TestController extends Controller
|
||||
{
|
||||
public function ok()
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (! app()->isProduction()) {
|
||||
auth()->login(User::where('email', 'nurmuhammet@mail.com')->first());
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
abort(404);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$models = [
|
||||
Order::class,
|
||||
Product::class,
|
||||
Attribute::class,
|
||||
Inventory::class,
|
||||
Channel::class,
|
||||
Collection::class,
|
||||
Category::class,
|
||||
Brand::class,
|
||||
User::class,
|
||||
// Payout::class,
|
||||
Banner::class,
|
||||
Carousel::class,
|
||||
Gallery::class,
|
||||
LegalPage::class,
|
||||
NewsletterUser::class,
|
||||
Newsletter::class,
|
||||
Coupon::class,
|
||||
Review::class,
|
||||
ContactUS::class,
|
||||
Role::class,
|
||||
Permission::class,
|
||||
Province::class,
|
||||
PostBranch::class,
|
||||
PaymentType::class,
|
||||
AppVersion::class,
|
||||
];
|
||||
|
||||
$data = [];
|
||||
collect($models)->each(function (string $model) use (&$data) {
|
||||
$modelNamespace = str_replace(
|
||||
'\\',
|
||||
'/',
|
||||
str_replace('App\\Models\\', '', $model)
|
||||
);
|
||||
$modelName = $modelNamespace.'Policy';
|
||||
|
||||
$data[] = [
|
||||
'key' => $modelName,
|
||||
'value' => $modelNamespace,
|
||||
];
|
||||
|
||||
Artisan::call("make:policy {$modelName} --model={$modelNamespace}");
|
||||
});
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
35
app/Http/Controllers/UserDocController.php
Normal file
35
app/Http/Controllers/UserDocController.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
|
||||
class UserDocController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$files = scandir(public_path($request->folder));
|
||||
|
||||
return Blade::render('
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Files</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
@foreach($lists as $list)
|
||||
<li><a href="{{ url($link_folder) }}/{{ $list }}">{{ $list }}</a></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
', [
|
||||
'link_folder' => $request->folder,
|
||||
'lists' => array_filter($files, fn ($data) => ! ($data == '.' || $data == '..')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
58
app/Http/Controllers/Web/Auth/LoginController.php
Normal file
58
app/Http/Controllers/Web/Auth/LoginController.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Auth\Verification;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
public function twoFactorLogin(Request $request)
|
||||
{
|
||||
$request->merge(['phone_number' => substr(str_replace(['+', '(', ')', '-'], '', $request->phone_number), 3)]);
|
||||
|
||||
$request->validate([
|
||||
'phone_number' => ['required', 'integer', 'between:61000000,65999999'],
|
||||
'verification_code' => ['nullable', 'integer'],
|
||||
]);
|
||||
|
||||
if ($request->filled('verification_code')) {
|
||||
return $this->login($request);
|
||||
}
|
||||
|
||||
sendSMSVerification($request->phone_number);
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$verification = Verification::where('username', $request->phone_number)
|
||||
->where('code', $request->verification_code)
|
||||
->first();
|
||||
|
||||
if (! $verification) {
|
||||
return response()->rest([], 401, 'Verification not found');
|
||||
}
|
||||
|
||||
$user = User::firstOrCreate(
|
||||
['phone_number' => $request->phone_number],
|
||||
[
|
||||
'first_name' => 'Ulanyjy',
|
||||
'last_name' => 'Ulanyjy',
|
||||
'email' => $request->phone_number.'user@fakemail.com',
|
||||
]
|
||||
);
|
||||
|
||||
auth()->login($user);
|
||||
|
||||
return response()->rest([], 200, 'Login');
|
||||
}
|
||||
}
|
||||
21
app/Http/Controllers/Web/Auth/LogoutController.php
Normal file
21
app/Http/Controllers/Web/Auth/LogoutController.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class LogoutController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
Auth::guard()->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect()->route('web.home');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\BecomeSeller;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Web\BecomeSellerPageStoreRequest;
|
||||
use App\Models\EntrepreneursDocs;
|
||||
use App\Models\User;
|
||||
use App\Models\Verification;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BecomeSellerPageController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('web.themes.shella.pages.becomeseller.index');
|
||||
}
|
||||
|
||||
public function store(BecomeSellerPageStoreRequest $request)
|
||||
{
|
||||
$user = User::create([
|
||||
'first_name' => $request->first_name,
|
||||
'last_name' => $request->last_name,
|
||||
'email' => $request->email,
|
||||
'password' => bcrypt($request->password),
|
||||
'phone_number' => $request->phone_number,
|
||||
]);
|
||||
|
||||
$user->addresses()->forceCreate([
|
||||
'first_name' => $request->first_name,
|
||||
'last_name' => $request->last_name,
|
||||
'company_name' => $request->shop_name,
|
||||
'street_address' => $request->street_address,
|
||||
'zipcode' => 744000,
|
||||
'city' => $request->city,
|
||||
'phone_number' => $request->phone_number,
|
||||
'is_default' => true,
|
||||
'type' => 'work',
|
||||
]);
|
||||
|
||||
EntrepreneursDocs::create([
|
||||
'corporation_type' => $request->corporation_type,
|
||||
'corporation_name' => $request->corporation_name,
|
||||
'patent_data' => str_replace('public/', '', $request->file('patent_data')?->store('public/entrepreneur/patent_data') ?? 'public/'),
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
sendSMSVerification($request->phone_number);
|
||||
|
||||
$verification = Verification::where(['username' => $request->email])->first();
|
||||
$verification ? $verification->update(['code' => 12345]) : Verification::create(['username' => $request->email, 'code' => 12345]);
|
||||
|
||||
return to_route('web.become-seller.verification', ['phone' => $request->phone_number, 'email' => $request->email]);
|
||||
}
|
||||
|
||||
public function verification()
|
||||
{
|
||||
return view('web.themes.shella.pages.becomeseller.verification');
|
||||
}
|
||||
|
||||
public function verificationStore(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone_number' => ['required', 'integer', 'between:61000000,65999999'],
|
||||
'phone_verification_code' => ['required', 'string'],
|
||||
'email' => ['required', 'string', 'email', 'exists:users,email'],
|
||||
'email_verification_code' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
if (Verification::where('username', $request->phone_number)->where('code', $request->phone_verification_code)->exists()
|
||||
&& Verification::where('username', $request->email)->where('code', $request->email_verification_code)->exists()
|
||||
) {
|
||||
$user = User::where([
|
||||
'phone_number' => $request->phone_number,
|
||||
'email' => $request->email,
|
||||
])->update([
|
||||
'phone_number_verified_at' => now(),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
event(new Verified($user));
|
||||
|
||||
session()->flash('sweet_alert_message', true);
|
||||
|
||||
session()->flash('sweet_alert_message_title', 'Üstünlikli ýerine ýetirildi');
|
||||
session()->flash('sweet_alert_message_content', '');
|
||||
session()->flash('sweet_alert_message_status', 'success');
|
||||
|
||||
return to_route('web.home');
|
||||
}
|
||||
|
||||
return back()->withErrors([
|
||||
'Verification' => 'could not pass validation',
|
||||
]);
|
||||
}
|
||||
}
|
||||
41
app/Http/Controllers/Web/Brand/BrandController.php
Normal file
41
app/Http/Controllers/Web/Brand/BrandController.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Brand;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Brand\Brand;
|
||||
use App\Repositories\Ecommerce\Product\ProductRepository;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function show(Brand $brand)
|
||||
{
|
||||
return to_route('web.brands.products', ['brand' => $brand->slug]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brand products
|
||||
*/
|
||||
public function products(Brand $brand, Request $request)
|
||||
{
|
||||
$products = ProductRepository::make($request)
|
||||
->queryAsFromResource($brand)
|
||||
->applyBasicQueries()
|
||||
->applyFilters()
|
||||
->applySorting()
|
||||
->simplePaginate();
|
||||
|
||||
return $request->ajax()
|
||||
? ProductRepository::ajaxPaginate($products)
|
||||
: view('web.themes.shella.pages.brands.products.index', [
|
||||
'brand' => $brand,
|
||||
'products' => $products,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Favorites;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class FavoritesPageController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$favs = auth()->user()->favorites()->with(['product' => ['media', 'brand']])->get();
|
||||
$products = $favs->map(fn ($fav) => $fav->product);
|
||||
|
||||
return view('web.themes.shella.pages.favorites.index', [
|
||||
'products' => $products,
|
||||
'user_likes' => $products,
|
||||
]);
|
||||
}
|
||||
}
|
||||
16
app/Http/Controllers/Web/Gallery/GalleryController.php
Normal file
16
app/Http/Controllers/Web/Gallery/GalleryController.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Gallery;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Site\Gallery;
|
||||
|
||||
class GalleryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('web.themes.shella.pages.galleries.index', [
|
||||
'galleries' => Gallery::with('media')->enabled()->latest()->get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
59
app/Http/Controllers/Web/Home/HomeController.php
Normal file
59
app/Http/Controllers/Web/Home/HomeController.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Home;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ecommerce\Product\Collection\Collection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$collections = Collection::query()
|
||||
->ordered()
|
||||
->where('is_visible', true)
|
||||
->simplePaginate(
|
||||
perPage: 6,
|
||||
page: request('page') ?: 1
|
||||
);
|
||||
|
||||
return $request->ajax()
|
||||
? $this->ajaxPaginate($collections)
|
||||
: view('web.themes.shella.pages.home.index', [
|
||||
'collections' => $collections,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax paginate
|
||||
*
|
||||
* @param Collection $collections
|
||||
*/
|
||||
public function ajaxPaginate($collections): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'pagination' => $collections,
|
||||
'collections' => view('web.themes.trendyol.components.products.collection.collections', [
|
||||
'collections' => $collections,
|
||||
])->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function userLikes()
|
||||
{
|
||||
$user_likes = auth()->check()
|
||||
? auth()->user()->favorites()->with(['media', 'brand'])->get()
|
||||
: collect();
|
||||
|
||||
cache()->put('user_favorites_likes_count', $user_likes->count());
|
||||
|
||||
return $user_likes;
|
||||
}
|
||||
}
|
||||
60
app/Http/Controllers/Web/LikeController.php
Normal file
60
app/Http/Controllers/Web/LikeController.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LikeController extends Controller
|
||||
{
|
||||
public function count()
|
||||
{
|
||||
return response()->json([
|
||||
'count' => auth()->user()->favorites()->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function userLikes()
|
||||
{
|
||||
return response()->rest(
|
||||
auth()->user()->favorites()->with(['product' => ['media', 'brand']])->limit(4)->get()
|
||||
->map(fn ($cartItem) => [
|
||||
'id' => $cartItem->product->id,
|
||||
'slug' => $cartItem->product->slug,
|
||||
'name' => $cartItem->product->name,
|
||||
'price_amount' => $cartItem->product->price_amount,
|
||||
'brand_id' => $cartItem->product->brand_id,
|
||||
'created_at' => $cartItem->product->created_at,
|
||||
'brand' => $cartItem->product->brand,
|
||||
'image' => $cartItem->product->thumbnail('200x200'),
|
||||
]));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => ['required', 'integer', 'exists:products,id'],
|
||||
]);
|
||||
|
||||
$data = auth()->user()->favorites()->where('product_id', $request->id)->first();
|
||||
|
||||
$status = $data
|
||||
? $data->delete()
|
||||
: auth()->user()->favorites()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'product_id' => $request->id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'attached' => is_bool($status) ? false : true,
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
DB::table('favorites')->where('user_id', auth()->id())->delete();
|
||||
|
||||
return response()->rest();
|
||||
}
|
||||
}
|
||||
21
app/Http/Controllers/Web/Locale/LocaleController.php
Normal file
21
app/Http/Controllers/Web/Locale/LocaleController.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Locale;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class LocaleController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function __invoke($locale)
|
||||
{
|
||||
if (array_key_exists($locale, config('app.locales'))) {
|
||||
session()->put('locale', $locale);
|
||||
}
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Marketing;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CMS\Marketing\NewsletterUser;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class NewsletterSubscriptionController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'email' => ['required', 'string', 'email', 'unique:newsletter_users'],
|
||||
]);
|
||||
|
||||
NewsletterUser::create(['email' => $request->email]);
|
||||
|
||||
return $request->ajax() ? ['success' => true] : back()->with('message', 'Successfully subscribed');
|
||||
}
|
||||
}
|
||||
15
app/Http/Controllers/Web/PaymentController.php
Normal file
15
app/Http/Controllers/Web/PaymentController.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
//
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('web.payment');
|
||||
}
|
||||
}
|
||||
14
app/Http/Controllers/Web/PostBranch/BranchController.php
Normal file
14
app/Http/Controllers/Web/PostBranch/BranchController.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\PostBranch;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Post\PostBranch;
|
||||
|
||||
class BranchController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return PostBranch::get(['id', 'province_id', 'name']);
|
||||
}
|
||||
}
|
||||
39
app/Http/Controllers/Web/ProfileController.php
Normal file
39
app/Http/Controllers/Web/ProfileController.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('web.themes.shella.pages.profile.index');
|
||||
}
|
||||
|
||||
public function edit()
|
||||
{
|
||||
return view('web.profile.edit');
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'first_name' => 'required|string',
|
||||
'last_name' => 'required|string',
|
||||
'password' => 'nullable|min:6',
|
||||
'phone_number' => ['required', 'integer', 'between:61000000,65999999', Rule::unique('users', 'phone_number')->ignore(auth()->id())],
|
||||
'email' => ['required', 'email', Rule::unique('users', 'email')->ignore(auth()->id())],
|
||||
]);
|
||||
|
||||
if (isset($data['password'])) {
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
}
|
||||
|
||||
auth()->user()->update(array_diff($data, [null]));
|
||||
|
||||
return redirect()->route('web.profile.index');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user