Moved from "app/src" to "app/application".
Otherwise phpstorm doesn't understand the paths correctly. He thinks that this is not a complete application, but a package. And when creating a class, the namespace indicates “app” with a small letter, but should be “App”.
This commit is contained in:
18
app/application/.editorconfig
Normal file
18
app/application/.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
|
74
app/application/.env.example
Normal file
74
app/application/.env.example
Normal file
@@ -0,0 +1,74 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_TIMEZONE=UTC
|
||||
APP_URL=http://localhost
|
||||
|
||||
CAPTCHA_API_DOMAIN=http://your-domain-captcha-or-IP:8081
|
||||
CAPTCHA_PRIVATE_TOKEN=
|
||||
CAPTCHA_STATIC_PATH=http://your-domain-captcha-or-IP:8081/captcha
|
||||
CAPTCHA_PUBLIC_TOKEN=
|
||||
|
||||
APP_FORCE_HTTPS=false
|
||||
|
||||
APP_DEFAULT_LOCALE=ru
|
||||
APP_FAKER_LOCALE=ru_RU
|
||||
APP_DEFAULT_USER_TIMEZONE=UTC
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
APP_MAINTENANCE_STORE=database
|
||||
|
||||
#LOGIN_MAX_REQUEST=50
|
||||
#LOGIN_MAX_EMAIL_REQUEST=10
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=redis
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=true
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=redis
|
||||
|
||||
CACHE_STORE=redis
|
||||
CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=app-redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
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
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
11
app/application/.gitattributes
vendored
Normal file
11
app/application/.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
|
19
app/application/.gitignore
vendored
Normal file
19
app/application/.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/.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
|
66
app/application/README.md
Normal file
66
app/application/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[WebReinvent](https://webreinvent.com/)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Jump24](https://jump24.co.uk)**
|
||||
- **[Redberry](https://redberry.international/laravel/)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
- **[byte5](https://byte5.de)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
94
app/application/app/Console/Commands/CreateUserAdmin.php
Normal file
94
app/application/app/Console/Commands/CreateUserAdmin.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Dto\User\ManyRoleDto;
|
||||
use App\Enums\SystemRole;
|
||||
use App\Repositories\RoleRepository;
|
||||
use App\Services\User\UserCommandHandler;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
final class CreateUserAdmin extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:create-user-admin {email} {password}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create admin user.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(UserCommandHandler $userCommandHandler, RoleRepository $roleRepository): void
|
||||
{
|
||||
$validator = $this->getData();
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->errorMessageAndStop($validator->errors()->all());
|
||||
}
|
||||
$data = $validator->valid();
|
||||
|
||||
try {
|
||||
$role = $roleRepository->getRoleByCode(SystemRole::Admin->value);
|
||||
if (is_null($role)) {
|
||||
$this->errorMessageAndStop('Administrator role not found.');
|
||||
}
|
||||
$user = DB::transaction(function () use($data, $userCommandHandler, $role) {
|
||||
$data['name'] = 'Administrator';
|
||||
$user = $userCommandHandler->handleStore($data, $data['password']);
|
||||
$userCommandHandler->handleConfirmationByEmail($user);
|
||||
$roles = new ManyRoleDto([$role->id]);
|
||||
$userCommandHandler->handleSyncRoles($user, $roles);
|
||||
|
||||
return $user;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->errorMessageAndStop($e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('The command was successful!');
|
||||
}
|
||||
|
||||
private function getData(): Validator
|
||||
{
|
||||
return ValidatorFacade::make([
|
||||
'email' => $this->argument('email'),
|
||||
'password' => $this->argument('password'),
|
||||
], [
|
||||
'email' => ['required', 'email', 'unique:users,email'],
|
||||
'password' => ['required', Password::default()],
|
||||
]);
|
||||
}
|
||||
|
||||
private function stop(): never
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
private function errorMessageAndStop(string | array $error): never
|
||||
{
|
||||
$this->info('User not created. See error messages below:');
|
||||
|
||||
if (is_array($error)) {
|
||||
foreach ($error as $err) {
|
||||
$this->error($err);
|
||||
}
|
||||
} else {
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
$this->stop();
|
||||
}
|
||||
}
|
10
app/application/app/Contracts/FormRequestDto.php
Normal file
10
app/application/app/Contracts/FormRequestDto.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
interface FormRequestDto
|
||||
{
|
||||
public function getDto(): Dto;
|
||||
}
|
15
app/application/app/Contracts/Models/Storage.php
Normal file
15
app/application/app/Contracts/Models/Storage.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Models;
|
||||
|
||||
use App\Enums\StorageType;
|
||||
use App\Models\Storage as StorageModel;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface Storage
|
||||
{
|
||||
public function storage(): MorphMany;
|
||||
public function getStorageOne(StorageType $type): ?StorageModel;
|
||||
public function getStorageMany(StorageType $type): Collection;
|
||||
}
|
18
app/application/app/Contracts/Search.php
Normal file
18
app/application/app/Contracts/Search.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Pagination\CursorPaginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface Search
|
||||
{
|
||||
public function __construct(Relation | Builder $query);
|
||||
public function all(): Collection;
|
||||
public function get(int $limit): Collection;
|
||||
public function pagination(int $limit, int $page = 1): LengthAwarePaginator;
|
||||
public function cursorPaginate(int $limit): CursorPaginator;
|
||||
}
|
10
app/application/app/Contracts/ServiceResult.php
Normal file
10
app/application/app/Contracts/ServiceResult.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
interface ServiceResult
|
||||
{
|
||||
public function isSuccess(): bool;
|
||||
public function isError(): bool;
|
||||
}
|
12
app/application/app/Contracts/ServiceResultError.php
Normal file
12
app/application/app/Contracts/ServiceResultError.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
interface ServiceResultError
|
||||
{
|
||||
public function getCode(): ?int;
|
||||
public function getMessage(): string;
|
||||
public function getErrors(): array;
|
||||
public function getErrorsOrMessage(): array|string;
|
||||
public function getData(): array;
|
||||
}
|
11
app/application/app/Contracts/StorageType.php
Normal file
11
app/application/app/Contracts/StorageType.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
interface StorageType
|
||||
{
|
||||
public function getTitle(): string;
|
||||
public function getAcceptMimes(): array;
|
||||
public function getFolderName(): string;
|
||||
public static function cases(): array;
|
||||
}
|
10
app/application/app/Contracts/StorageType/Audio.php
Normal file
10
app/application/app/Contracts/StorageType/Audio.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\StorageType;
|
||||
|
||||
use App\Contracts\StorageType as StorageTypeContract;
|
||||
|
||||
interface Audio extends StorageTypeContract
|
||||
{
|
||||
public function isAudio(): bool;
|
||||
}
|
10
app/application/app/Contracts/StorageType/Image.php
Normal file
10
app/application/app/Contracts/StorageType/Image.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\StorageType;
|
||||
|
||||
use App\Contracts\StorageType as StorageTypeContract;
|
||||
|
||||
interface Image extends StorageTypeContract
|
||||
{
|
||||
public function isImage(): bool;
|
||||
}
|
10
app/application/app/Contracts/StorageType/Video.php
Normal file
10
app/application/app/Contracts/StorageType/Video.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\StorageType;
|
||||
|
||||
use App\Contracts\StorageType as StorageTypeContract;
|
||||
|
||||
interface Video extends StorageTypeContract
|
||||
{
|
||||
public function isVideo(): bool;
|
||||
}
|
10
app/application/app/Dto/Builder/Project.php
Normal file
10
app/application/app/Dto/Builder/Project.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
final readonly class Project
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
) { }
|
||||
}
|
10
app/application/app/Dto/Builder/Role.php
Normal file
10
app/application/app/Dto/Builder/Role.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
final readonly class Role
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
) { }
|
||||
}
|
10
app/application/app/Dto/Builder/User.php
Normal file
10
app/application/app/Dto/Builder/User.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
final readonly class User
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
) { }
|
||||
}
|
27
app/application/app/Dto/QuerySettingsDto.php
Normal file
27
app/application/app/Dto/QuerySettingsDto.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto;
|
||||
|
||||
final readonly class QuerySettingsDto
|
||||
{
|
||||
public function __construct(
|
||||
private int $limit,
|
||||
private int $page = 1,
|
||||
private array $queryWith = []
|
||||
) { }
|
||||
|
||||
public function getLimit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function getQueryWith(): array
|
||||
{
|
||||
return $this->queryWith;
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Language;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class NewLanguage extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private int $index,
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getIndex(): int
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
}
|
21
app/application/app/Dto/Service/Admin/Project/Index.php
Normal file
21
app/application/app/Dto/Service/Admin/Project/Index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project;
|
||||
|
||||
use App\Dto\Builder\Project;
|
||||
use App\Dto\Service\Pages;
|
||||
|
||||
final readonly class Index extends Pages
|
||||
{
|
||||
public function __construct(
|
||||
private Project $projectBuilderDto,
|
||||
int $page
|
||||
) {
|
||||
parent::__construct($page);
|
||||
}
|
||||
|
||||
public function getProjectBuilderDto(): Project
|
||||
{
|
||||
return $this->projectBuilderDto;
|
||||
}
|
||||
}
|
41
app/application/app/Dto/Service/Admin/Project/Language.php
Normal file
41
app/application/app/Dto/Service/Admin/Project/Language.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class Language extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $title,
|
||||
private string $code,
|
||||
private int $sort,
|
||||
private bool $isDefault,
|
||||
private ?int $id,
|
||||
) { }
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCode(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getSort(): int
|
||||
{
|
||||
return $this->sort;
|
||||
}
|
||||
|
||||
public function isDefault(): bool
|
||||
{
|
||||
return $this->isDefault;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
18
app/application/app/Dto/Service/Admin/Project/Languages.php
Normal file
18
app/application/app/Dto/Service/Admin/Project/Languages.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project;
|
||||
|
||||
final class Languages
|
||||
{
|
||||
private $languages = [];
|
||||
|
||||
public function addLanguage(Language $language): void
|
||||
{
|
||||
$this->languages[] = $language;
|
||||
}
|
||||
|
||||
public function getLanguages(): array
|
||||
{
|
||||
return $this->languages;
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $code,
|
||||
private bool $isPublic,
|
||||
private Languages $languages,
|
||||
private Storages $storages,
|
||||
private ?string $httpHost,
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getCode(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function isPublic(): bool
|
||||
{
|
||||
return $this->isPublic;
|
||||
}
|
||||
|
||||
public function getLanguages(): Languages
|
||||
{
|
||||
return $this->languages;
|
||||
}
|
||||
|
||||
public function getHttpHost(): ?string
|
||||
{
|
||||
return $this->httpHost;
|
||||
}
|
||||
|
||||
public function getStorages(): Storages
|
||||
{
|
||||
return $this->storages;
|
||||
}
|
||||
}
|
21
app/application/app/Dto/Service/Admin/Role/Index.php
Normal file
21
app/application/app/Dto/Service/Admin/Role/Index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Role;
|
||||
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Service\Pages;
|
||||
|
||||
final readonly class Index extends Pages
|
||||
{
|
||||
public function __construct(
|
||||
private Role $roleBuilderDto,
|
||||
int $page
|
||||
) {
|
||||
parent::__construct($page);
|
||||
}
|
||||
|
||||
public function getRoleBuilderDto(): Role
|
||||
{
|
||||
return $this->roleBuilderDto;
|
||||
}
|
||||
}
|
29
app/application/app/Dto/Service/Admin/Role/StoreUpdate.php
Normal file
29
app/application/app/Dto/Service/Admin/Role/StoreUpdate.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Role;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private ?string $code,
|
||||
private array $permissions,
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return $this->permissions;
|
||||
}
|
||||
}
|
21
app/application/app/Dto/Service/Admin/User/Index.php
Normal file
21
app/application/app/Dto/Service/Admin/User/Index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\User;
|
||||
|
||||
use App\Dto\Builder\User;
|
||||
use App\Dto\Service\Pages;
|
||||
|
||||
final readonly class Index extends Pages
|
||||
{
|
||||
public function __construct(
|
||||
private User $userBuilderDto,
|
||||
int $page
|
||||
) {
|
||||
parent::__construct($page);
|
||||
}
|
||||
|
||||
public function getUserBuilderDto(): User
|
||||
{
|
||||
return $this->userBuilderDto;
|
||||
}
|
||||
}
|
36
app/application/app/Dto/Service/Admin/User/StoreUpdate.php
Normal file
36
app/application/app/Dto/Service/Admin/User/StoreUpdate.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\User;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Dto\User\ManyRoleDto;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $email,
|
||||
private ManyRoleDto $roles,
|
||||
private ?string $password = null
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getRoles(): ManyRoleDto
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
}
|
27
app/application/app/Dto/Service/Authorization.php
Normal file
27
app/application/app/Dto/Service/Authorization.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
final readonly class Authorization extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $email,
|
||||
private string $password,
|
||||
private bool $remember = false
|
||||
) { }
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getRemember(): bool
|
||||
{
|
||||
return $this->remember;
|
||||
}
|
||||
}
|
8
app/application/app/Dto/Service/Dto.php
Normal file
8
app/application/app/Dto/Service/Dto.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
abstract readonly class Dto
|
||||
{
|
||||
|
||||
}
|
15
app/application/app/Dto/Service/Pages.php
Normal file
15
app/application/app/Dto/Service/Pages.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
abstract readonly class Pages extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private int $page
|
||||
) { }
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
}
|
17
app/application/app/Dto/Service/Private/Profile/Update.php
Normal file
17
app/application/app/Dto/Service/Private/Profile/Update.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Private\Profile;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class Update extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Private\Profile;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Enums\Lang;
|
||||
|
||||
final readonly class UpdateSettings extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private ?Lang $lang,
|
||||
private ?string $timezone
|
||||
) { }
|
||||
|
||||
public function getLang(): ?Lang
|
||||
{
|
||||
return $this->lang;
|
||||
}
|
||||
|
||||
public function getTimezone(): ?string
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
}
|
29
app/application/app/Dto/Service/Storage/File.php
Normal file
29
app/application/app/Dto/Service/Storage/File.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Storage;
|
||||
|
||||
use App\Contracts\StorageType;
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Exceptions\Dto\Storage\FileException;
|
||||
|
||||
final readonly class File extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private int $id,
|
||||
private StorageType $storageType,
|
||||
) {
|
||||
if ($this->id < 1) {
|
||||
throw new FileException('ID cannot be equal to or less than zero: ' . $this->id);
|
||||
}
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getStorageType(): StorageType
|
||||
{
|
||||
return $this->storageType;
|
||||
}
|
||||
}
|
57
app/application/app/Dto/Service/Storage/Storage.php
Normal file
57
app/application/app/Dto/Service/Storage/Storage.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Storage;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Enums\StorageType;
|
||||
use app\Exceptions\Dto\Storage\StorageException;
|
||||
|
||||
final readonly class Storage extends Dto
|
||||
{
|
||||
private ?File $file;
|
||||
private bool $isDelete;
|
||||
|
||||
public function __construct(
|
||||
array $data,
|
||||
private StorageType $storageType,
|
||||
private bool $isMany,
|
||||
) {
|
||||
$file = null;
|
||||
if (isset($data['file'])) {
|
||||
if ((int) $data['file'] <= 0) {
|
||||
throw new StorageException('ID cannot be equal to or less than zero: ' . $data['file']);
|
||||
}
|
||||
$file = new File(
|
||||
id: (int) $data['file'],
|
||||
storageType: $this->storageType,
|
||||
);
|
||||
}
|
||||
$this->file = $file;
|
||||
$this->isDelete = !empty($data['delete']);
|
||||
}
|
||||
|
||||
public function getFile(): ?File
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function isDelete(): bool
|
||||
{
|
||||
return $this->isDelete;
|
||||
}
|
||||
|
||||
public function isFile(): bool
|
||||
{
|
||||
return \is_null($this->file) === false;
|
||||
}
|
||||
|
||||
public function getStorageType(): StorageType
|
||||
{
|
||||
return $this->storageType;
|
||||
}
|
||||
|
||||
public function isMany(): bool
|
||||
{
|
||||
return $this->isMany;
|
||||
}
|
||||
}
|
73
app/application/app/Dto/Service/Storage/Storages.php
Normal file
73
app/application/app/Dto/Service/Storage/Storages.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Storage;
|
||||
|
||||
use App\Enums\StorageType;
|
||||
use app\Exceptions\Dto\Storage\StoragesException;
|
||||
|
||||
final class Storages
|
||||
{
|
||||
private array $storages = [];
|
||||
|
||||
public function add(array $data, StorageType $type): void
|
||||
{
|
||||
if (isset($this->storages[$type->value])) {
|
||||
throw new StoragesException('You cannot attach two files of the same type!');
|
||||
}
|
||||
$this->storages[$type->value] = new Storage(data: $data, storageType: $type, isMany: false);
|
||||
}
|
||||
|
||||
public function addMany(array $data, StorageType $type): void
|
||||
{
|
||||
if (!isset($this->storages[$type->value])) {
|
||||
$this->storages[$type->value] = [];
|
||||
}
|
||||
|
||||
foreach ($data as $storageData) {
|
||||
$this->storages[$type->value][] = new Storage(data: $storageData, storageType: $type, isMany: true);
|
||||
}
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->storages;
|
||||
}
|
||||
|
||||
public function getAllStorageIds(): array
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($this->storages as $storage) {
|
||||
/** @var Storage $storage */
|
||||
if (!is_array($storage)) {
|
||||
if ($storage->isFile() && !$storage->isDelete()) {
|
||||
$ids[] = $storage->getFile()->getId();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
foreach ($storage as $storageOne) {
|
||||
/** @var Storage $storageOne */
|
||||
if ($storageOne->isFile() && !$storageOne->isDelete()) {
|
||||
$ids[] = $storageOne->getFile()->getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
public function getAllStorages(): array
|
||||
{
|
||||
$storages = [];
|
||||
foreach ($this->storages as $storage) {
|
||||
if (!is_array($storage)) {
|
||||
$storages[] = $storage;
|
||||
continue;
|
||||
}
|
||||
foreach ($storage as $storageOne) {
|
||||
$storages[] = $storageOne;
|
||||
}
|
||||
}
|
||||
|
||||
return $storages;
|
||||
}
|
||||
}
|
32
app/application/app/Dto/Service/Storage/Upload.php
Normal file
32
app/application/app/Dto/Service/Storage/Upload.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Storage;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Enums\Morph;
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
final readonly class Upload extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private UploadedFile $file,
|
||||
private StorageType $storageType,
|
||||
private Morph $morph,
|
||||
) { }
|
||||
|
||||
public function getFile(): UploadedFile
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function getStorageType(): StorageType
|
||||
{
|
||||
return $this->storageType;
|
||||
}
|
||||
|
||||
public function getMorph(): Morph
|
||||
{
|
||||
return $this->morph;
|
||||
}
|
||||
}
|
17
app/application/app/Dto/Service/User/UpdatePassword.php
Normal file
17
app/application/app/Dto/Service/User/UpdatePassword.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\User;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class UpdatePassword extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $password
|
||||
) { }
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
32
app/application/app/Dto/User/ManyRoleDto.php
Normal file
32
app/application/app/Dto/User/ManyRoleDto.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\User;
|
||||
|
||||
use App\Exceptions\Dto\User\ManyRoleDtoException;
|
||||
|
||||
final class ManyRoleDto
|
||||
{
|
||||
private array $roles = [];
|
||||
|
||||
public function __construct(array $roles = []) {
|
||||
foreach ($roles as $role) {
|
||||
if (!is_numeric($role) || is_float($role)) {
|
||||
throw new ManyRoleDtoException('Not an integer: ' . $role . '.');
|
||||
}
|
||||
$this->add((int) $role);
|
||||
}
|
||||
}
|
||||
|
||||
public function add(int $id): void
|
||||
{
|
||||
if ($id < 1) {
|
||||
throw new ManyRoleDtoException('Only Integer > 0.');
|
||||
}
|
||||
$this->roles[] = $id;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
}
|
46
app/application/app/Enums/Lang.php
Normal file
46
app/application/app/Enums/Lang.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
enum Lang: int
|
||||
{
|
||||
case Ru = 1;
|
||||
case En = 2;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Ru => 'Русский',
|
||||
self::En => 'English'
|
||||
};
|
||||
}
|
||||
|
||||
public function getLocale(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Ru => 'ru',
|
||||
self::En => 'en'
|
||||
};
|
||||
}
|
||||
|
||||
public static function toArray(): array
|
||||
{
|
||||
$choices = [];
|
||||
foreach (self::cases() as $lang) {
|
||||
$choices[] = [
|
||||
'name' => $lang->name,
|
||||
'value' => $lang->value,
|
||||
'title' => $lang->getTitle(),
|
||||
'locale' => $lang->getLocale()
|
||||
];
|
||||
}
|
||||
return $choices;
|
||||
}
|
||||
|
||||
public static function toCollection(): Collection
|
||||
{
|
||||
return collect(self::toArray());
|
||||
}
|
||||
}
|
31
app/application/app/Enums/Morph.php
Normal file
31
app/application/app/Enums/Morph.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Models\Project;
|
||||
|
||||
enum Morph: int
|
||||
{
|
||||
case Project = 1;
|
||||
|
||||
public function getPathModel(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Project => Project::class,
|
||||
};
|
||||
}
|
||||
|
||||
public function getFolderName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public static function map(): array
|
||||
{
|
||||
$map = [];
|
||||
foreach (self::cases() as $item) {
|
||||
$map[$item->value] = $item->getPathModel();
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
}
|
62
app/application/app/Enums/Permission.php
Normal file
62
app/application/app/Enums/Permission.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum Permission: string
|
||||
{
|
||||
case AdminPanel = 'allow-admin-panel';
|
||||
case Role = 'role';
|
||||
case User = 'user';
|
||||
case Project = 'project';
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
$permissions = match ($this) {
|
||||
self::AdminPanel => [
|
||||
'view' => __('permissions.Administrative panel allowed'),
|
||||
],
|
||||
default => $this->getBasePermissions()
|
||||
};
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return __('permissions.' . $this->name);
|
||||
}
|
||||
|
||||
public function formatValue(string $permission): string
|
||||
{
|
||||
return $this->value . '.' . $permission;
|
||||
}
|
||||
|
||||
public static function toArrayList(): array
|
||||
{
|
||||
$permissions = [];
|
||||
foreach (self::cases() as $permissionEnum) {
|
||||
foreach ($permissionEnum->getPermissions() as $permissionName => $permissionTitle) {
|
||||
$name = $permissionEnum->formatValue($permissionName);
|
||||
$title = $permissionEnum->getTitle() . ' - ' . $permissionTitle;
|
||||
$permissions[$name] = $title;
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
public static function toArrayListCodes(): array
|
||||
{
|
||||
return \array_keys(self::toArrayList());
|
||||
}
|
||||
|
||||
private function getBasePermissions(): array
|
||||
{
|
||||
return [
|
||||
'view' => __('permissions.Allowed to watch'),
|
||||
'create' => __('permissions.Allowed to create'),
|
||||
'update' => __('permissions.Allowed to edit'),
|
||||
'delete' => __('permissions.Allowed to delete'),
|
||||
];
|
||||
}
|
||||
}
|
53
app/application/app/Enums/StorageType.php
Normal file
53
app/application/app/Enums/StorageType.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Contracts\StorageType\Audio;
|
||||
use App\Contracts\StorageType\Image;
|
||||
use App\Contracts\StorageType\Video;
|
||||
|
||||
enum StorageType: int implements Image, Video, Audio
|
||||
{
|
||||
case Logo = 1;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => __('validation.attributes.logo'),
|
||||
};
|
||||
}
|
||||
|
||||
public function getAcceptMimes(): array
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => ['jpeg', 'jpg', 'png'],
|
||||
};
|
||||
}
|
||||
|
||||
public function isImage(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => true,
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
||||
public function isVideo(): bool
|
||||
{
|
||||
return match ($this->name) {
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
||||
public function isAudio(): bool
|
||||
{
|
||||
return match ($this->name) {
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
||||
public function getFolderName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
9
app/application/app/Enums/SystemRole.php
Normal file
9
app/application/app/Enums/SystemRole.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SystemRole: string
|
||||
{
|
||||
case Admin = 'admin';
|
||||
case AdminProject = 'admin-project';
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Dto\Storage;
|
||||
|
||||
final class FileException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace app\Exceptions\Dto\Storage;
|
||||
|
||||
final class StorageException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace app\Exceptions\Dto\Storage;
|
||||
|
||||
final class StoragesException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Dto\User;
|
||||
|
||||
final class ManyRoleDtoException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace app\Exceptions\Services\Storage;
|
||||
|
||||
final class StorageCommandHandlerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Services\Storage;
|
||||
|
||||
final class StorageSaveFileException extends \Exception
|
||||
{
|
||||
|
||||
}
|
40
app/application/app/Helpers/Helpers.php
Normal file
40
app/application/app/Helpers/Helpers.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class Helpers
|
||||
{
|
||||
public static function getTimeZoneList(): Collection
|
||||
{
|
||||
return Cache::rememberForever('timezones_list_collection', function () {
|
||||
$timezone = [];
|
||||
foreach (timezone_identifiers_list(\DateTimeZone::ALL) as $key => $value) {
|
||||
$timezone[$value] = $value . ' (UTC ' . now($value)->format('P') . ')';
|
||||
}
|
||||
return collect($timezone)->sortKeys();
|
||||
});
|
||||
}
|
||||
|
||||
public static function getUserTimeZone() {
|
||||
return auth()->user()?->timezone ?? config('app.user_timezone');
|
||||
}
|
||||
|
||||
/**
|
||||
* $name = 'field[key]' return 'field.key'
|
||||
*/
|
||||
public static function formatAttributeNameToRequestName(string $name): string
|
||||
{
|
||||
return Str::of($name)
|
||||
->replace(
|
||||
['.', '[', ']'],
|
||||
['_', '.', ''],
|
||||
)
|
||||
->rtrim('.')
|
||||
->value();
|
||||
}
|
||||
}
|
10
app/application/app/Http/Controllers/Admin/Controller.php
Normal file
10
app/application/app/Http/Controllers/Admin/Controller.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class DashboardController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return view('admin/dashboard/index');
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\Languages\NewLanguageRequest;
|
||||
use App\Http\Resources\Admin\Languages\NewLanguage;
|
||||
use App\Services\Admin\LanguageService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
final class LanguagesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LanguageService $languageService,
|
||||
) { }
|
||||
|
||||
public function newLanguage(NewLanguageRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->languageService->newLanguage($request->getDto());
|
||||
if ($result->isError()) {
|
||||
return response()->json($result->getData())->setStatusCode($result->getCode());
|
||||
}
|
||||
|
||||
return response()->json(new NewLanguage($result));
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Admin\Projects\IndexRequest;
|
||||
use App\Http\Requests\Admin\Projects\StoreUpdateRequest;
|
||||
use App\Services\Admin\ProjectService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class ProjectsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectService $projectService
|
||||
) { }
|
||||
|
||||
public function index(IndexRequest $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$data = $request->getDto();
|
||||
$querySettingsDto = new QuerySettingsDto(
|
||||
limit: 20,
|
||||
page: $data->getPage(),
|
||||
queryWith: []
|
||||
);
|
||||
|
||||
$result = $this->projectService->index($data->getProjectBuilderDto(), $querySettingsDto, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/projects/index', $result->getData());
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->projectService->create($user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/projects/create', $result->getData());
|
||||
}
|
||||
|
||||
public function edit(int $id, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->projectService->edit($id, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/projects/edit', $result->getData());
|
||||
}
|
||||
|
||||
public function store(StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->projectService->store($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.projects.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->projectService->update($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.projects.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function destroy(int $id, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->projectService->destroy($id, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.projects.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Admin\Roles\IndexRequest;
|
||||
use App\Http\Requests\Admin\Roles\StoreUpdateRequest;
|
||||
use App\Services\Admin\RoleService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class RolesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RoleService $roleService
|
||||
) { }
|
||||
public function index(IndexRequest $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$data = $request->getDto();
|
||||
$querySettingsDto = new QuerySettingsDto(
|
||||
limit: 20,
|
||||
page: $data->getPage(),
|
||||
queryWith: ['morph']
|
||||
);
|
||||
|
||||
$result = $this->roleService->index($data->getRoleBuilderDto(), $querySettingsDto, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/index', $result->getData());
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->create($user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/create', $result->getData());
|
||||
}
|
||||
|
||||
public function edit(int $id, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->edit($id, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/edit', $result->getData());
|
||||
}
|
||||
|
||||
public function store(StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->store($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->update($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function destroy(int $id, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->destroy($id, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
106
app/application/app/Http/Controllers/Admin/UsersController.php
Normal file
106
app/application/app/Http/Controllers/Admin/UsersController.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Admin\Users\IndexRequest;
|
||||
use App\Http\Requests\Admin\Users\StoreUpdateRequest;
|
||||
use App\Http\Requests\Admin\Users\UpdatePasswordRequest;
|
||||
use App\Services\Admin\UserService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class UsersController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserService $userService
|
||||
) { }
|
||||
|
||||
public function index(IndexRequest $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$data = $request->getDto();
|
||||
$querySettingsDto = new QuerySettingsDto(
|
||||
limit: 20,
|
||||
page: $data->getPage(),
|
||||
queryWith: []
|
||||
);
|
||||
|
||||
$result = $this->userService->index($data->getUserBuilderDto(), $querySettingsDto, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/index', $result->getData());
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->create($user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/create', $result->getData());
|
||||
}
|
||||
|
||||
public function edit(int $id, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->edit($id, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/edit', $result->getData());
|
||||
}
|
||||
|
||||
public function store(StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->store($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->update($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updatePassword(int $id, UpdatePasswordRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->updatePassword($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function destroy(int $id, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->destroy($id, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
47
app/application/app/Http/Controllers/AuthController.php
Normal file
47
app/application/app/Http/Controllers/AuthController.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AuthorizationRequest;
|
||||
use App\Services\AuthService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
final class AuthController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AuthService $authService
|
||||
) { }
|
||||
|
||||
public function login(): View
|
||||
{
|
||||
return view('login');
|
||||
}
|
||||
|
||||
public function authorization(AuthorizationRequest $request): RedirectResponse
|
||||
{
|
||||
$authorization = $request->getDto();
|
||||
$result = $this->authService->authorization($authorization);
|
||||
if ($result->isError()) {
|
||||
if ($result->getCode() === Response::HTTP_UNAUTHORIZED) {
|
||||
Log::warning('Unauthorized ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||
}
|
||||
return redirect()->route('login')->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
$request->session()->regenerate();
|
||||
Log::notice('Logged in ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||
return redirect()->route('admin.home');
|
||||
}
|
||||
|
||||
public function logout(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
return redirect(route('login'));
|
||||
}
|
||||
}
|
18
app/application/app/Http/Controllers/Controller.php
Normal file
18
app/application/app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Contracts\ServiceResultError as ServiceResultErrorContract;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
final protected function errors(ServiceResultErrorContract $result): never
|
||||
{
|
||||
if ($result->getCode() === Response::HTTP_UNPROCESSABLE_ENTITY) {
|
||||
redirect()->back()->withInput()->withErrors($result->getErrors());
|
||||
exit;
|
||||
}
|
||||
abort($result->getCode(), $result->getMessage());
|
||||
}
|
||||
}
|
10
app/application/app/Http/Controllers/Private/Controller.php
Normal file
10
app/application/app/Http/Controllers/Private/Controller.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Private;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Private;
|
||||
|
||||
use App\Enums\Lang;
|
||||
use App\Helpers\Helpers;
|
||||
use App\Http\Requests\Private\Profile\UpdatePasswordRequest;
|
||||
use App\Http\Requests\Private\Profile\UpdateRequest;
|
||||
use App\Http\Requests\Private\Profile\UpdateSettingsRequest;
|
||||
use App\Services\Private\ProfileService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class ProfileController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProfileService $profileService
|
||||
) { }
|
||||
|
||||
public function profile(Request $request): View
|
||||
{
|
||||
return view('private/profile/profile', [
|
||||
'user' => $request->user()
|
||||
]);
|
||||
}
|
||||
|
||||
public function settings(Request $request): View
|
||||
{
|
||||
return view('private/profile/settings', [
|
||||
'user' => $request->user(),
|
||||
'languages' => Lang::toCollection()->pluck(value: 'title', key: 'value')->toArray(),
|
||||
'timezone' => Helpers::getTimeZoneList()->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->update($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.edit')->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updatePassword(UpdatePasswordRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->updatePassword($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.edit')->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updateSettings(UpdateSettingsRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->updateSettings($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.settings')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
10
app/application/app/Http/Controllers/Storage/Controller.php
Normal file
10
app/application/app/Http/Controllers/Storage/Controller.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Storage;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\Http\Controllers\Storage;
|
||||
|
||||
use App\Http\Requests\Storage\ImageRequest;
|
||||
use App\Http\Resources\Storage\Upload;
|
||||
use App\Services\Storage\ImageService;
|
||||
|
||||
final class ImagesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ImageService $imageService,
|
||||
) { }
|
||||
|
||||
public function uploadAndResize(ImageRequest $request)
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->imageService->uploadAndResize($data, $user);
|
||||
|
||||
if (!$result->isSuccess()) {
|
||||
return response()->json($result->getData())->setStatusCode($result->getCode());
|
||||
}
|
||||
|
||||
return response()->json(new Upload($result->getStorage()));
|
||||
}
|
||||
}
|
26
app/application/app/Http/Middleware/AdminPanel.php
Normal file
26
app/application/app/Http/Middleware/AdminPanel.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class AdminPanel
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (
|
||||
$request->user() === null || $request->user()->cannot('AdminPanel')
|
||||
) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
19
app/application/app/Http/Middleware/UserLocale.php
Normal file
19
app/application/app/Http/Middleware/UserLocale.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
final class UserLocale
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($request->user() && $request->user()->lang) {
|
||||
App::setLocale($request->user()->lang->getLocale());
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Languages;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Language\NewLanguage;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class NewLanguageRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'index' => ['required', 'numeric'],
|
||||
'name' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): NewLanguage
|
||||
{
|
||||
return new NewLanguage(
|
||||
name: $this->input('name'),
|
||||
index: $this->input('index'),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Projects;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\Project;
|
||||
use App\Dto\Service\Admin\Project\Index;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$this->redirect = route('admin.projects.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
projectBuilderDto: new Project(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Projects;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Project\Language;
|
||||
use App\Dto\Service\Admin\Project\Languages;
|
||||
use App\Dto\Service\Admin\Project\StoreUpdate;
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
use App\Enums\StorageType;
|
||||
use App\Rules\HttpHost;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'code' => ['required', 'string', 'min:3', 'max:255', 'regex:/^[a-z0-9_-]+$/i'],
|
||||
'http_host' => ['nullable', 'string', 'max:255', new HttpHost()],
|
||||
'is_public' => ['required', 'boolean'],
|
||||
|
||||
'logo.file' => ['nullable', 'numeric', 'min:1'],
|
||||
'logo.delete' => ['nullable', 'boolean'],
|
||||
|
||||
'languages.items.*.id' => ['nullable', 'numeric'],
|
||||
'languages.items.*.title' => ['required', 'string', 'max:255'],
|
||||
'languages.items.*.code' => ['required', 'string', 'min:2', 'max:30', 'regex:/^[a-zA-Z_]+$/i'],
|
||||
'languages.items.*.sort' => ['required', 'numeric', 'min:-1000', 'max:1000'],
|
||||
'languages.default' => ['required', 'numeric', function (string $attribute, mixed $value, \Closure $fail) {
|
||||
$languages = $this->input('languages.items', []);
|
||||
if (!isset($languages[$value])) {
|
||||
$this->validator->getMessageBag()->add('languages.default', __('validation.required', ['attribute' => __('validation.attributes.language-default')]));
|
||||
}
|
||||
}],
|
||||
'languages.items' => ['required', 'array', function (string $attribute, mixed $value, \Closure $fail) {
|
||||
$values = [];
|
||||
foreach ($value as $index => $item) {
|
||||
if (!isset($item['code'])) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($item['code'], $values)) {
|
||||
$this->validator->getMessageBag()->add('languages.items.' . $index . '.code', __('validation.unique', ['attribute' => __('validation.attributes.language-code')]));
|
||||
}
|
||||
$values[] = $item['code'];
|
||||
}
|
||||
}],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): StoreUpdate
|
||||
{
|
||||
return new StoreUpdate(
|
||||
name: $this->input('name'),
|
||||
code: $this->input('code'),
|
||||
isPublic: (bool) $this->input('is_public', false),
|
||||
languages: $this->languages(),
|
||||
storages: $this->storages(),
|
||||
httpHost: $this->input('http_host', null),
|
||||
);
|
||||
}
|
||||
|
||||
private function languages(): Languages
|
||||
{
|
||||
$languages = new Languages();
|
||||
|
||||
$default = $this->input('languages.default', null);
|
||||
if ($default !== null) {
|
||||
$default = (int) $default;
|
||||
}
|
||||
|
||||
foreach ($this->input('languages.items', []) as $index => $lang) {
|
||||
$languageId = $lang['id'] ?? null;
|
||||
if ($languageId !== null) {
|
||||
$languageId = (int) $languageId;
|
||||
}
|
||||
$language = new Language(
|
||||
title: $lang['title'],
|
||||
code: $lang['code'],
|
||||
sort: (int) $lang['sort'],
|
||||
isDefault: ($default === $index),
|
||||
id: $languageId,
|
||||
);
|
||||
$languages->addLanguage($language);
|
||||
}
|
||||
|
||||
return $languages;
|
||||
}
|
||||
|
||||
private function storages(): Storages
|
||||
{
|
||||
$storages = new Storages();
|
||||
|
||||
$logo = $this->get('logo', []);
|
||||
$storages->add($logo, StorageType::Logo);
|
||||
|
||||
return $storages;
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Service\Admin\Role\Index;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$this->redirect = route('admin.roles.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
roleBuilderDto: new Role(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Role\StoreUpdate;
|
||||
use App\Rules\Permission;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => ['required', 'max:255'],
|
||||
'permissions' => ['array', new Permission()]
|
||||
];
|
||||
if ($this->getMethod() === 'POST') {
|
||||
$rules['code'] = ['required', 'min:3', 'max:255', 'regex:/^[a-z0-9_-]+$/i'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): StoreUpdate
|
||||
{
|
||||
return new StoreUpdate(
|
||||
name: $this->input('name'),
|
||||
code: $this->input('code', null),
|
||||
permissions: $this->input('permissions', [])
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\User;
|
||||
use App\Dto\Service\Admin\User\Index;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$this->redirect = route('admin.users.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
userBuilderDto: new User(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\User\StoreUpdate;
|
||||
use App\Dto\User\ManyRoleDto;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => ['required', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'roles' => ['array', Rule::exists('roles', 'id')],
|
||||
];
|
||||
|
||||
if ($this->getMethod() === 'POST') {
|
||||
$rules['password'] = ['required', Password::default()];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): StoreUpdate
|
||||
{
|
||||
return new StoreUpdate(
|
||||
name: $this->input('name'),
|
||||
email: $this->input('email'),
|
||||
roles: new ManyRoleDto($this->input('roles', [])),
|
||||
password: $this->input('password', null),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class UpdatePasswordRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'password' => ['required', Password::default()],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdatePassword
|
||||
{
|
||||
return new UpdatePassword(password: $this->input('password'));
|
||||
}
|
||||
}
|
32
app/application/app/Http/Requests/AuthorizationRequest.php
Normal file
32
app/application/app/Http/Requests/AuthorizationRequest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Authorization;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class AuthorizationRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'password' => ['required', 'min:3'],
|
||||
'captcha-verified' => ['captcha'],
|
||||
'remember' => ['nullable', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Authorization
|
||||
{
|
||||
return new Authorization(
|
||||
email: $this->input('email'),
|
||||
password: $this->input('password'),
|
||||
remember: (bool) $this->input('remember', false)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Private\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class UpdatePasswordRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'password' => ['required', 'confirmed', Password::default()],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdatePassword
|
||||
{
|
||||
return new UpdatePassword(password: $this->input('password'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Private\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Private\Profile\Update;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class UpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Update
|
||||
{
|
||||
return new Update(name: $this->input('name'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Private\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Private\Profile\UpdateSettings;
|
||||
use App\Enums\Lang;
|
||||
use App\Helpers\Helpers;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
final class UpdateSettingsRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'lang' => ['nullable', new Enum(Lang::class)],
|
||||
'timezone' => ['nullable', Rule::in(Helpers::getTimeZoneList()->keys()->toArray())]
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdateSettings
|
||||
{
|
||||
$lang = $this->input('lang', null);
|
||||
if (!is_null($lang)) {
|
||||
$lang = Lang::from((int) $lang);
|
||||
}
|
||||
|
||||
return new UpdateSettings(
|
||||
lang: $lang,
|
||||
timezone: $this->input('timezone', null),
|
||||
);
|
||||
}
|
||||
}
|
40
app/application/app/Http/Requests/Storage/ImageRequest.php
Normal file
40
app/application/app/Http/Requests/Storage/ImageRequest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Storage;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Storage\Upload;
|
||||
use App\Enums\Morph;
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
final class ImageRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$mimes = '';
|
||||
if ($this->has('storage_type')) {
|
||||
$storageType = StorageType::tryFrom((int) $this->input('storage_type')) ?? [];
|
||||
$mimes = implode(',', $storageType->getAcceptMimes());
|
||||
}
|
||||
|
||||
return [
|
||||
'file' => ['required', 'file', 'mimes:' . $mimes],
|
||||
'storage_type' => ['required', 'numeric', new Enum(StorageType::class)],
|
||||
'morph' => ['required', 'numeric', new Enum(Morph::class)],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Upload
|
||||
{
|
||||
return new Upload(
|
||||
file: $this->file('file'),
|
||||
storageType: StorageType::from((int) $this->input('storage_type')),
|
||||
morph: Morph::from((int) $this->input('morph')),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Admin\Languages;
|
||||
|
||||
use App\ServiceResults\Admin\LanguageService\NewLanguageResult;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class NewLanguage extends JsonResource
|
||||
{
|
||||
/**
|
||||
* @var NewLanguageResult
|
||||
*/
|
||||
public $resource;
|
||||
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'html' => view('components.volt.forms.languages.language', [
|
||||
'index' => $this->resource->getIndex(),
|
||||
'name' => $this->resource->getName(),
|
||||
'lang' => $this->resource->getLanguage()->toArray(),
|
||||
])->render(),
|
||||
];
|
||||
}
|
||||
}
|
23
app/application/app/Http/Resources/Storage/Upload.php
Normal file
23
app/application/app/Http/Resources/Storage/Upload.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Storage;
|
||||
|
||||
use App\Models\Storage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class Upload extends JsonResource
|
||||
{
|
||||
/**
|
||||
* @var Storage
|
||||
*/
|
||||
public $resource;
|
||||
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->resource->id,
|
||||
'url' => $this->resource->url,
|
||||
];
|
||||
}
|
||||
}
|
50
app/application/app/Models/Project.php
Normal file
50
app/application/app/Models/Project.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\StorageTrait;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Contracts\Models\Storage as StorageContract;
|
||||
|
||||
final class Project extends Model implements StorageContract
|
||||
{
|
||||
use HasFactory, SoftDeletes, StorageTrait;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'code',
|
||||
'http_host',
|
||||
'is_public'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_public' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function roles(): MorphMany
|
||||
{
|
||||
return $this->morphMany(Role::class, 'morph');
|
||||
}
|
||||
|
||||
public function languages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProjectLanguage::class);
|
||||
}
|
||||
}
|
50
app/application/app/Models/ProjectLanguage.php
Normal file
50
app/application/app/Models/ProjectLanguage.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Scopes\SortScope;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||
|
||||
#[ScopedBy([SortScope::class])]
|
||||
final class ProjectLanguage extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The model's default values for attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'is_default' => false,
|
||||
'sort' => 100,
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'code',
|
||||
'is_default',
|
||||
'sort',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_default' => 'boolean',
|
||||
'sort' => 'integer',
|
||||
];
|
||||
}
|
||||
}
|
75
app/application/app/Models/Role.php
Normal file
75
app/application/app/Models/Role.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SystemRole as SystemRoleEnum;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\hasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
final class Role extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'code',
|
||||
];
|
||||
|
||||
public function scopeLatest(Builder $query): Builder
|
||||
{
|
||||
return $query->orderBy('id', 'desc');
|
||||
}
|
||||
|
||||
public function scopeAlphavit(Builder $query): Builder
|
||||
{
|
||||
return $query->orderBy('name', 'asc');
|
||||
}
|
||||
|
||||
protected function nameWithMorph(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
$name = $this->name;
|
||||
if ($this->morph_type) {
|
||||
$name .= ' (' . __('admin-sections.Projects') . ': ' . $this->morph?->name . ')';
|
||||
}
|
||||
|
||||
return $name;
|
||||
},
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
public function morph(): MorphTo
|
||||
{
|
||||
return $this->morphTo('morph');
|
||||
}
|
||||
|
||||
public function permissions(): hasMany
|
||||
{
|
||||
return $this->hasMany(RolePermission::class, 'role_id', 'id');
|
||||
}
|
||||
|
||||
protected function isRemove(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($dontRemove) => ( SystemRoleEnum::tryFrom($this->code) === null ),
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
protected function isAdmin(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => ( \is_null($this->morphable_type) && \is_null($this->morphable_id) && $this->code === SystemRoleEnum::Admin->value ),
|
||||
)->shouldCache();
|
||||
}
|
||||
}
|
21
app/application/app/Models/RolePermission.php
Normal file
21
app/application/app/Models/RolePermission.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class RolePermission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'role_permission';
|
||||
|
||||
public function getPermissionTitleAttribute(): string
|
||||
{
|
||||
$pemissions = Permission::toArrayList();
|
||||
|
||||
return $pemissions[$this->permission] ?? '';
|
||||
}
|
||||
}
|
18
app/application/app/Models/Scopes/SortScope.php
Normal file
18
app/application/app/Models/Scopes/SortScope.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Scopes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Scope;
|
||||
|
||||
final class SortScope implements Scope
|
||||
{
|
||||
/**
|
||||
* Apply the scope to a given Eloquent query builder.
|
||||
*/
|
||||
public function apply(Builder $builder, Model $model): void
|
||||
{
|
||||
$builder->orderBy('sort', 'asc');
|
||||
}
|
||||
}
|
70
app/application/app/Models/Storage.php
Normal file
70
app/application/app/Models/Storage.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage as StorageSupport;
|
||||
|
||||
class Storage extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'storage';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'data',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'data' => 'array',
|
||||
'type' => StorageType::class,
|
||||
];
|
||||
}
|
||||
|
||||
protected function dataImages(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->data['images'] ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
protected function url(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if (\is_null($this->file)) {
|
||||
return '';
|
||||
}
|
||||
return StorageSupport::disk(config('storage.disk'))->url($this->file) . '?time=' . $this->updated_at->getTimestamp();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected function path(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if (\is_null($this->file)) {
|
||||
return '';
|
||||
}
|
||||
return StorageSupport::disk(config('storage.disk'))->path($this->file);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
26
app/application/app/Models/Traits/StorageTrait.php
Normal file
26
app/application/app/Models/Traits/StorageTrait.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Traits;
|
||||
|
||||
use App\Enums\StorageType;
|
||||
use App\Models\Storage as StorageModel;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
trait StorageTrait
|
||||
{
|
||||
public function storage(): MorphMany
|
||||
{
|
||||
return $this->morphMany(StorageModel::class, 'morph');
|
||||
}
|
||||
|
||||
public function getStorageOne(StorageType $type): ?StorageModel
|
||||
{
|
||||
return $this->storage->firstWhere('type', $type);
|
||||
}
|
||||
|
||||
public function getStorageMany(StorageType $type): Collection
|
||||
{
|
||||
return $this->storage->where('type', $type);
|
||||
}
|
||||
}
|
87
app/application/app/Models/User.php
Normal file
87
app/application/app/Models/User.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Enums\Lang;
|
||||
use App\Enums\SystemRole;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
final class User extends Authenticatable
|
||||
{
|
||||
use HasFactory, Notifiable, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'nickname',
|
||||
'timezone',
|
||||
'lang',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'lang' => Lang::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function roles(): belongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class);
|
||||
}
|
||||
|
||||
public function hasPermission(string $permission): bool
|
||||
{
|
||||
return $this->permissions->search($permission) !== false;
|
||||
}
|
||||
|
||||
protected function isAdmin(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->roles
|
||||
->where('code', SystemRole::Admin->value)
|
||||
->whereNull('morphable_type')
|
||||
->whereNull('morphable_id')
|
||||
->isNotEmpty(),
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
protected function permissions(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
$roles = $this->roles->modelKeys();
|
||||
return RolePermission::query()->whereIn('role_id', $roles)->select('permission')->pluck('permission');
|
||||
},
|
||||
)->shouldCache();
|
||||
}
|
||||
}
|
16
app/application/app/Policies/AdminPanel.php
Normal file
16
app/application/app/Policies/AdminPanel.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\SystemRole;
|
||||
use App\Models\User;
|
||||
|
||||
readonly class AdminPanel extends Policy
|
||||
{
|
||||
public function view(User $user): bool
|
||||
{
|
||||
return
|
||||
$user->hasPermission('allow-admin-panel.view')
|
||||
|| $user->roles->where('code', SystemRole::AdminProject->value)->isNotEmpty();
|
||||
}
|
||||
}
|
20
app/application/app/Policies/Policy.php
Normal file
20
app/application/app/Policies/Policy.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
abstract readonly class Policy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
final public function before(User $user): ?bool
|
||||
{
|
||||
if ($user->is_admin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
43
app/application/app/Policies/ProjectPolicy.php
Normal file
43
app/application/app/Policies/ProjectPolicy.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class ProjectPolicy extends Policy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('project.view');
|
||||
}
|
||||
|
||||
public function view(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermission('project.view');
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('project.create');
|
||||
}
|
||||
|
||||
public function update(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermission('project.update');
|
||||
}
|
||||
|
||||
public function delete(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermission('project.delete');
|
||||
}
|
||||
|
||||
public function upload(User $user): bool
|
||||
{
|
||||
if ($user->hasPermission('project.create') || $user->hasPermission('project.update')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
34
app/application/app/Policies/RolePolicy.php
Normal file
34
app/application/app/Policies/RolePolicy.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class RolePolicy extends Policy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('role.view');
|
||||
}
|
||||
|
||||
public function view(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.view');
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('role.create');
|
||||
}
|
||||
|
||||
public function update(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.update');
|
||||
}
|
||||
|
||||
public function delete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.delete');
|
||||
}
|
||||
}
|
33
app/application/app/Policies/UserPolicy.php
Normal file
33
app/application/app/Policies/UserPolicy.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class UserPolicy extends Policy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('user.view');
|
||||
}
|
||||
|
||||
public function view(User $user, User $userView): bool
|
||||
{
|
||||
return $user->hasPermission('user.view');
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('user.create');
|
||||
}
|
||||
|
||||
public function update(User $user, User $userUpdate): bool
|
||||
{
|
||||
return $user->hasPermission('user.update');
|
||||
}
|
||||
|
||||
public function delete(User $user, User $userDelete): bool
|
||||
{
|
||||
return $user->hasPermission('user.delete');
|
||||
}
|
||||
}
|
92
app/application/app/Providers/AppServiceProvider.php
Normal file
92
app/application/app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Enums\Morph;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Services\Search\Search;
|
||||
use App\Services\Storage\Image\ResizeCommandHandler;
|
||||
use App\Services\Storage\ImageService;
|
||||
use App\Services\Storage\StorageCommandHandler;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(CreateSearchInstanceCommand::class, function () {
|
||||
return new CreateSearchInstanceCommand(Search::class);
|
||||
});
|
||||
|
||||
$this->app->bind(StorageCommandHandler::class, function () {
|
||||
return new StorageCommandHandler(disc: (string) config('storage.disk'));
|
||||
});
|
||||
|
||||
$this->app->bind(ImageService::class, function (Application $app) {
|
||||
return new ImageService(
|
||||
storageCommandHandler: $app->make(StorageCommandHandler::class),
|
||||
resizeCommandHandler: $app->make(ResizeCommandHandler::class),
|
||||
maxImageWidth: (int) config('storage.max_image_width', 4000),
|
||||
maxImageHeight: (int) config('storage.max_image_height', 4000),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
if (config('app.force_https') === true) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
$this->passwordDefaults();
|
||||
|
||||
Relation::enforceMorphMap(Morph::map());
|
||||
|
||||
$this->configureRateLimiting();
|
||||
Gate::define('AdminPanel', [\App\Policies\AdminPanel::class, 'view']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*/
|
||||
private function configureRateLimiting(): void
|
||||
{
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
return [
|
||||
Limit::perHour(config('rate_limiting.login_max_request', 50))->by($request->getClientIp()),
|
||||
Limit::perHour(config('rate_limiting.login_max_email_request', 10))->by($request->getClientIp() . '-' . $request->input('email')),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
private function passwordDefaults(): void
|
||||
{
|
||||
Password::defaults(function () {
|
||||
$rule = Password::min(8);
|
||||
|
||||
if ($this->app->isProduction()) {
|
||||
$rule->letters()
|
||||
->mixedCase()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
});
|
||||
}
|
||||
}
|
49
app/application/app/Repositories/ProjectRepository.php
Normal file
49
app/application/app/Repositories/ProjectRepository.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Dto\Builder\Project as ProjectBuilderDto;
|
||||
use App\Models\Project;
|
||||
use App\Services\Project\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class ProjectRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getProjects(ProjectBuilderDto $projectBuilderDto, array $with = []): Search
|
||||
{
|
||||
$query = $this->builderCommand->execute(
|
||||
query: Project::query()->with($with),
|
||||
projectBuilderDto: $projectBuilderDto
|
||||
);
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getProjectById(int $id): ?Project
|
||||
{
|
||||
return Project::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getProjectByCode(string $code): ?Project
|
||||
{
|
||||
return Project::query()->where('code', $code)->first();
|
||||
}
|
||||
|
||||
public function isExistsCode(string $code, ?int $exceptId = null): bool
|
||||
{
|
||||
return Project::query()
|
||||
->where('code', $code)
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
}
|
61
app/application/app/Repositories/RoleRepository.php
Normal file
61
app/application/app/Repositories/RoleRepository.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\Role;
|
||||
use App\Dto\Builder\Role as RoleBuilderDto;
|
||||
use App\Services\Role\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class RoleRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getRoleById(int $id): ?Role
|
||||
{
|
||||
return Role::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getRoleByCode(string $code): ?Role
|
||||
{
|
||||
return Role::query()->where('code', $code)->first();
|
||||
}
|
||||
|
||||
public function getRoles(RoleBuilderDto $roleBuilderDto, array $with = []): Search
|
||||
{
|
||||
$query = $this->builderCommand->execute(
|
||||
query: Role::query()->with($with),
|
||||
roleBuilderDto: $roleBuilderDto
|
||||
);
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getRolesForSelect(array $withExcepts = []): array
|
||||
{
|
||||
$roles = Role::query()
|
||||
->with(['morph'])
|
||||
->when($withExcepts, function (Builder $query, array $withExcepts) {
|
||||
$query->withTrashed()->whereNull('deleted_at')->orWhereIn('id', $withExcepts);
|
||||
})->get();
|
||||
return $roles->pluck('name_with_morph', 'id')->toArray();
|
||||
}
|
||||
|
||||
public function isExistsCode(string $code, ?int $exceptId = null): bool
|
||||
{
|
||||
return Role::query()
|
||||
->where('code', $code)
|
||||
->whereNull('morphable_type')
|
||||
->whereNull('morphable_id')
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
}
|
25
app/application/app/Repositories/StorageRepository.php
Normal file
25
app/application/app/Repositories/StorageRepository.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\Storage;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Contracts\Search;
|
||||
|
||||
final readonly class StorageRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
) { }
|
||||
|
||||
public function getStorageById(int $id): ?Storage
|
||||
{
|
||||
return Storage::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getStoregsByIds(array $ids): Search
|
||||
{
|
||||
$query = Storage::query()->whereIn('id', $ids);
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
}
|
50
app/application/app/Repositories/UserRepository.php
Normal file
50
app/application/app/Repositories/UserRepository.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\User;
|
||||
use App\Dto\Builder\User as UserBuilderDto;
|
||||
use App\Services\User\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class UserRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getUserById(int $id): ?User
|
||||
{
|
||||
return User::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getUserByEmail(string $email): ?User
|
||||
{
|
||||
return User::query()->where('email', Str::lower($email))->first();
|
||||
}
|
||||
|
||||
public function getUsers(UserBuilderDto $userBuilderDto, array $with = []): Search
|
||||
{
|
||||
$query = $this->builderCommand->execute(
|
||||
query: User::query()->with($with),
|
||||
userBuilderDto: $userBuilderDto
|
||||
);
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function isExistsEmail(string $email, ?int $exceptId = null): bool
|
||||
{
|
||||
return User::query()
|
||||
->where('email', Str::lower($email))
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
}
|
52
app/application/app/Rules/HttpHost.php
Normal file
52
app/application/app/Rules/HttpHost.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
final readonly class HttpHost implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!is_string($value) && !is_array($value)) {
|
||||
$fail('validation.no_type')->translate(['type' => 'string, array']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$this->validateHttpHost($value, $fail);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$this->validateHttpHost($item, $fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function validateHttpHost(string $value, Closure $fail): void
|
||||
{
|
||||
$scheme = explode('://', $value, 2);
|
||||
if (count($scheme) != 2 || in_array($scheme[0], ['http', 'https'], true) !== true) {
|
||||
$fail('validation.http_host')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
$host = explode(':', $scheme[1], 2);
|
||||
if (!filter_var($host[0], FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
|
||||
$fail('validation.http_host')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($host[1]) && (!is_numeric($host[1]) || $host[1] <= 0)) {
|
||||
$fail('validation.http_host')->translate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
55
app/application/app/Rules/Permission.php
Normal file
55
app/application/app/Rules/Permission.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
final readonly class Permission implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!is_string($value) && !is_array($value)) {
|
||||
$fail('validation.no_type')->translate(['type' => 'string, array']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$this->validatePermission($value, $fail);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$this->validatePermission($item, $fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function validatePermission(string $value, Closure $fail): void
|
||||
{
|
||||
$value = explode('.', $value, 2);
|
||||
|
||||
if (count($value) !== 2) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
list($permissionEnum, $permission) = $value;
|
||||
$permissionEnum = \App\Enums\Permission::tryFrom($permissionEnum);
|
||||
if (is_null($permissionEnum)) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
$permissions = $permissionEnum->getPermissions();
|
||||
if (!isset($permissions[$permission])) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user