mrzlanx532/laravel-basic-components is a Laravel package for laravel basic components.
It currently has 1 GitHub stars and 51 downloads on Packagist (latest version 1.1.16).
Install it with composer require mrzlanx532/laravel-basic-components.
Discover more Laravel packages by mrzlanx532
or browse all Laravel packages to compare alternatives.
Last updated
UploadImages (Mrzlanx532\LaravelBasicComponents\Traits\Model\UploadImages\UploadImages)UploadFile (Mrzlanx532\LaravelBasicComponents\Traits\Model\UploadFile\UploadFile)Service (Mrzlanx532\LaravelBasicComponents\Service)Definition (Mrzlanx532\LaravelBasicComponents\Definition\Definition)PanelForm (Mrzlanx532\LaravelBasicComponents\PanelForm\PanelForm)QueryBuilder (Mrzlanx532\LaravelBasicComponents\QueryBuilder)PanelSet (Mrzlanx532\LaravelBasicComponents\PanelSet\PanelSet)PanelSetSortable (Mrzlanx532\LaravelBasicComponents\PanelSetSortable\PanelSetSortable)BrowserFilterPresetController (Mrzlanx532\LaravelBasicComponents\Controllers\BrowserFilterPresetController)composer require mrzlanx532/laravel-basic-componentsphp artisan vendor:publish --tag="laravel-basic-components-config"php artisan vendor:publish --tag="laravel-basic-components-migrations"browser (компонент, который используется в админке), удаляем миграцию create_duotek_browser_filters_presets.php artisan migratemiddleware для группы api Mrzlanx532\LaravelBasicComponents\Middleware\ReplaceNullValuesInFormDataUTC (00:00), соответственно, в конфиге config.app должно быть:timezone => 'UTC'datetime и timestamp при сохранении должны приводится к UTC (00:00)UploadImages (Mrzlanx532\LaravelBasicComponents\Traits\Model\UploadImages\UploadImages)Request с учетом конфигурации в модели$filePropertiesWithSettings поместить конфиг по примеру ниже:...
use UploadImages;
...
/**
* Если есть константа UPLOAD_FILE_TRAIT_DELETING_FILES и она true,
* то при удалении модели также будут удаляться связанные с ней изображения (и нарезки)
*/
const UPLOAD_FILE_TRAIT_DELETING_FILES = true;
public static $filePropertiesWithSettings = [
'picture' => [
'200' => [200, 200, FileHelper::RESIZE_TYPE_SMART],
'400' => [400, 400, FileHelper::RESIZE_TYPE_FIT_INTO_AREA_WITH_PROPORTIONS],
'600' => [600, 600, FileHelper::RESIZE_TYPE_FIT_INTO_AREA_WITH_PROPORTIONS_AND_COLOR_CANVAS, '#ffffff', 'center'], // Возможные значение 4-го параметра: top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right
],
'background_picture' => null, // null означает сохранить как есть (без нарезок)
];
...
...
class UserResource extends JsonResource
{
/* @var $resource User */
public $resource;
/**
* @throws InvalidFilePropertiesWithSettingsPropertyConfiguration
*/
public function toArray($request): array
{
return [
'id' => $this->resource->id,
'photo' => $this->resource->getFileLinksBySettings('photo'),
'photo2' => $this->resource->getFileLinksBySettings('photo2', 'http://api.tip.ru'), // возможность кастомно задать домен, не смотря на UPLOAD_FILE_DOMAIN
];
}
}
getFileLinksBySettings можно:UPLOAD_FILE_DOMAIN=http://api.example.ru в .env файл в корне проекта->getFileLinksBySettings('photo', 'http://api.example.ru')...
use Mrzlanx532\LaravelBasicComponents\Helpers\FileHelper\FileHelper;
...
class UserResource extends JsonResource
{
/* @var $resource User */
public $resource;
/**
* @throws InvalidFilePropertiesWithSettingsPropertyConfiguration
*/
public function toArray($request): array
{
return [
'id' => $this->resource->id,
'photo1' => FileHelper::getFileLinksBySettings(User::class, 'photo1', $this->resource->photo1),
'photo2' => FileHelper::getFileLinksBySettings(User::class, 'photo2', $this->resource->photo2),
];
}
}
UploadFile (Mrzlanx532\LaravelBasicComponents\Traits\Model\UploadFile\UploadFile)Трейт применяется к смежным таблицам. (например: таблица user_files, где есть колонка user_id и file_id)
Позволяет удобно сохранять, обновлять, удалять файлы взаимодействуя с таблицей files.
Также позволяет удобно настраивать конфигурацию сохранения (см. ниже)
При обновление модели удаляет предыдущий файл и заменяет новым.
При удаление модели удаляет файл (если нету трейта SoftDeletes).
При удалении через forceDelete файл тоже физически удаляется.
user_files)<?php
namespace App\Models;
use App\Config\UploadFileConfig;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
use App\Traits\UploadFile;
/**
* \App\Models\UserFile
*
* @property int id
* @property int user_id
* @property int file_id
* @property Carbon|null deleted_at
*
* @property UploadedFile|null uploadFile
* @property-read File $file;
*
* @method static Builder|ContactFile query()
* @method static ContactFile|null find($id)
* @method static ContactFile findOrFail($id)
*
* @mixin Model
*/
class UserFile extends Model
{
use UploadFile; // Добавляем трейт
use SoftDeletes; // Добавляем SoftDeletes, если требуется
protected $table = 'user_files';
public $timestamps = false;
/**
* Если настройки по-умолчанию не подходят, то создаем метод getUploadFileConfig
*/
public function getUploadFileConfig(): UploadFileConfig
{
return UploadFileConfig::create()
->setAsPublic()
->setForeignKey('file_id');
}
}
...
$user = new User;
$user->save();
if (isset($this->params['files'])) {
foreach ($this->params['files'] as $passedFile) {
$file = new UserFile;
// Свойство `uploadFile` является зарезервированным.
// $passedFile является \Illuminate\Http\UploadedFile
$file->uploadFile = $passedFile;
$files[] = $file;
};
$user->files()->saveMany($files);
}
...
storage/app/private/files/<год>/<месяц>/<день>/<хэш_от_файла>.<расширение_файла>.files и создалась связь в таблице user_filesService (Mrzlanx532\LaravelBasicComponents\Service)Сервис является обязательным для использования при POST-действиях.
Например: Создание клиента, Изменение статуса заказа, Обновления данных клиента, Удаления клиента
namespace App\Services\Finance\BalanceInvoice;
use App\Models\Finance\BalanceInvoice as FinanceBalanceInvoice;
use Mrzlanx532\LaravelBasicComponents\Service\Service;
class FinanceBalanceInvoiceCreateService extends Service
{
public function getRules(): array
{
return [
'user_id' => 'required|int|exists:users_users,id',
'base_type_id' => 'required|string',
'offer_id' => 'required|int|exists:market_offers_offers,id',
'state_id' => 'required|string',
'total' => 'required|numeric',
];
}
public function handle(): FinanceBalanceInvoice
{
$balanceInvoice = new FinanceBalanceInvoice();
$balanceInvoice->user_id = $this->params['user_id'];
$balanceInvoice->offer_id = $this->params['offer_id'];
$balanceInvoice->base_type_id = $this->params['base_type_id'];
$balanceInvoice->state_id = $this->params['state_id'];
$balanceInvoice->total = $this->params['total'];
$balanceInvoice->save();
return $balanceInvoice;
}
}
...
public function create(Request $request, FinanceBalanceInvoiceCreateService $financeBalanceInvoiceCreateService): JsonResponse
{
return response()->json(
new FinanceBalanceInvoiceResource(
$financeBalanceInvoiceCreateService->setParams($request)->handle()
)
);
}
...
params в методе getRules доступны в чистом виде (всё что приходит с Request или array)params в методе handle очищены от полей, которые отсутствуют в getRulesDefinition (Mrzlanx532\LaravelBasicComponents\Definition\Definition)Используется для хранения констант в определенном формате.
Имеет множество методов для удобной работы с ними.
namespace App\Definitions\Finance\Balance;
use Mrzlanx532\LaravelBasicComponents\Definition\Definition;
class InvoiceBaseDefinition extends Definition
{
const OFFER = 'OFFER';
public static function items(): array
{
return [
self::OFFER => [
'id' => self::OFFER,
'title' => 'Оплата объявления',
],
];
}
}
title?
$definition::getItems(withLocale:true)
$definition::getItems(withLocale:true, specifiedLocale:'ru')
$definition::getItemByConst('RUS');
$definition::getItemByConst(const:'RUS', withLocale:true, specifiedLocale:'ru');
$definition::getItemByConst(const:'RUS', withLocale:true);
PanelForm (Mrzlanx532\LaravelBasicComponents\PanelForm\PanelForm)Используется в качестве соглашения с библиотекой на фронтенде для данных, которые должны быть получены для формы.
Используется исключительно в админках.
PanelForm
namespace App\PanelForms\Backoffice\Users;
use App\Http\Resources\Backoffice\Users\User\UserResource;
use App\Models\Users\User;
use Mrzlanx532\LaravelBasicComponents\PanelForm\PanelForm;
class UserPanelForm extends PanelForm
{
protected string $model = User::class;
protected string|null $resource = UserResource::class;
protected function getInputs(): array
{
return [];
}
}
...
public function form(UserPanelForm $userPanelForm): JsonResponse
{
return response()->json($userPanelForm->get());
}
...
QueryBuilder (Mrzlanx532\LaravelBasicComponents\QueryBuilder)Нужен для построения сложных запросов с участнием Illuminate\Http\Request и соответственно инкапсуляции логики
QueryBuildernamespace App\Http\QueryBuilders;
use App\Models\Finance\BalanceTransaction;
use Mrzlanx532\LaravelBasicComponents\QueryBuilder\QueryBuilder;
use Illuminate\Database\Eloquent\Collection;
class FinanceBalanceTransactionQueryBuilder extends QueryBuilder
{
public function handle(): Collection|array
{
$balanceTransactionQuery = BalanceTransaction::query();
if ($this->request->has('user_id')) {
$balanceTransactionQuery->where('user_id', $this->request->get('user_id'))
}
return $balanceTransactionQuery->get();
}
}
...
public function list(Request $request): JsonResponse
{
return response()->json(
FinanceBalanceTransactionResource::collection(
(new FinanceBalanceTransactionQueryBuilder($request))->handle()
)
);
}
...
params в методе getRules доступны в чистом виде (всё что приходит с Request или array)params в методе handle очищены от полей, которые отсутствуют в getRulesPanelSet (Mrzlanx532\LaravelBasicComponents\PanelSet\PanelSet)Используется в качестве соглашения с библиотекой на фронтенде для определения интерфейса на фронтенде через бэкенд.
Используется исключительно в админках.
PanelSet
namespace App\PanelSet;
use App\Http\Resources\Web\MarketOffer\MarketOfferResource;
use App\Models\Market\Offer\Offer as MarketOffer;
use Mrzlanx532\LaravelBasicComponents\PanelSet\Filters\BooleanFilter;
use Mrzlanx532\LaravelBasicComponents\PanelSet\Filters\SelectFilter;
use Mrzlanx532\LaravelBasicComponents\PanelSet\PanelSet;
class MarketOfferPanelSet extends PanelSet
{
protected string $model = MarketOffer::class;
public string $resource = MarketOfferResource::class;
public string $browserId = 'market_offers';
/**
* Если необходимо передать кастомный способ поиска отличный от LIKE, делаем как в примере 1
*/
public array $fieldsForDefaultSearchFilter = ['costs_type_id', 'payment_type_id'];
protected array $defaultOrderBy = [
'created_at' => 'desc',
// Пример с мапингом: принимаем `created_at`, а в запрос кладем `tips_codes.created_at`
// 'created_at as tips_codes.created_at' => 'desc'
// Для кастомизации смотри "Пример 2"
];
public array $availableOrderBy = [
'created_at',
// Пример с мапингом: принимаем `created_at`, а в запрос кладем `tips_codes.created_at`
// 'created_at as tips_codes.created_at'
// Для кастомизации смотри "Пример 3"
];
public function __construct()
{
parent::__construct();
/**
* Пример 1
*/
$this->fieldsForDefaultSearchFilter = [
'costs_type_id',
'payment_type_id',
function (Builder $query, string|null $searchString) {
$query->OrWhere('id', '=', $searchString);
}
];
/**
* Пример 2
*/
$this->defaultOrderBy = [
'created_at' => function (\Illuminate\Database\Eloquent\Builder $queryBuilder) {
// Пример кастомизации: здесь пишем как обработать поле 'updated_at'
$queryBuilder->orderByRaw('<RAW SQL QUERY>');
}
];
/**
* Пример 3
*/
$this->availableOrderBy = [
'created_at' => function (\Illuminate\Database\Eloquent\Builder $queryBuilder, $field, $direction) {
// Пример кастомизации: здесь пишем как обработать поле 'updated_at'
$queryBuilder->orderByRaw('<RAW SQL QUERY>');
}
];
}
protected function setFilters()
{
$this->filtersManager->add(SelectFilter::class, 'product_id', 'Продукт', function (SelectFilter $selectFilter) {
$exampleOptions = [
0 => [
'id' => 1,
'title' => 'Продукт1'
],
1 => [
'id' => 2,
'title' => 'Продукт2'
],
];
$selectFilter->setOptions($exampleOptions);
});
$this->filtersManager->add(BooleanFilter::class, 'is_price_per_one_is_set', 'Тест', function (BooleanFilter $booleanFilter) {
// В запрос передаем вместо 'is_price_per_one_is_set' => 'is_price_per_one_is'
$booleanFilter->setFilterParamName('is_price_per_one_is');
// Скрываем из интерфейса, но обрабатываем всё равно
$booleanFilter->hidden();
// Делаем фильтр обязательным
$booleanFilter->required();
});
// Пример кастомного запроса
$this->filtersManager->add(SelectFilter::class, 'randomValue(ни на что не влияет в случае кастома)', 'Участник', function (SelectFilter $selectFilter) {
$selectFilter
->setFilterParamName('user_id');
->setCustomQueryClosure(function (\Illuminate\Database\Eloquent\Builder $builder, $values) {
$builder
->where('market_deals.requester_user_id', $values[0])
->orWhere('market_deals.responser_user_id', $values[0]);
});
});
}
}
...
/**
* @throws InvalidJsonFormatForFiltersParameterException
* @throws InvalidPanelSetConfigurationException
*/
public function browse(MarketOfferPanelSet $marketOfferPanelSet): JsonResponse
{
return response()->json($marketOfferPanelSet->handle());
}
...
Пример из POSTMAN как управляться с фильтрами:

PanelSetSortable (Mrzlanx532\LaravelBasicComponents\PanelSetSortable\PanelSetSortable)Используется в качестве соглашения с библиотекой на фронтенде для определения интерфейса на фронтенде через бэкенд.
Нужен для компонента на фронте, который имеет Drag'n'Drop
Компонент определяет порядок записей
и может вкладывать одну запись в другую (опционально)
PanelSetSortable<?php
namespace App\PanelSets\Backoffice\Suppliers\Levels;
use App\Http\Resources\Backoffice\Suppliers\Levels\LevelListResource;
use App\Models\Suppliers\Levels\Level;
use Mrzlanx532\LaravelBasicComponents\PanelSetSortable\PanelSetSortable;
class LevelPanelSetSortable extends PanelSetSortable
{
protected string $model = Level::class;
public string $resource = LevelListResource::class;
/** [Опционально] Определяем нужна ли поддержка вложенности */
protected bool $isNested = false;
/** [Опционально] Колонка в таблице, которое будет отвечать за порядок */
protected string $orderField = 'order_index';
/** [Опционально] Колонка в таблице, которая определяет родителя при включенной вкложенности */
protected string $parentField = 'parent_id';
/** [Опционально] Колонка, содержащая в себе первичный ключ таблицы */
protected string $identifierField = 'id';
}
use App\PanelSets\Backoffice\Suppliers\Levels\LevelPanelSetSortable;
use Illuminate\Http\JsonResponse;
/**
* GET
* suppliers/levels/browse-sortable
* [backoffice-api]
* Браузер уровней поставщиков сортированный
*
* @param LevelPanelSetSortable $levelPanelSetSortable
* @return JsonResponse
*/
public function browseSortable(LevelPanelSetSortable $levelPanelSetSortable): JsonResponse
{
return response()->json($levelPanelSetSortable->handle());
}
use Mrzlanx532\LaravelBasicComponents\Service\PanelSetSortableUpdateService\PanelSetSortableUpdateService;
use App\PanelSets\Backoffice\Suppliers\Levels\LevelPanelSetSortable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* POST
* suppliers/levels/browse-sortable/update
* [backoffice-api]
* Обновить сортировку браузера
*
* @bodyParam items object[] required
* @bodyParam items[].id int required
* @bodyParam items[].parent int required (если вложенность отключена, убираем из доки)
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function browseSortableUpdate(Request $request): JsonResponse
{
(new PanelSetSortableUpdateService(LevelPanelSetSortable::class))->setParams($request)->handle();
return response()->json(['status' => true]);
}
BrowserFilterPresetController (Mrzlanx532\LaravelBasicComponents\Controllers\BrowserFilterPresetController)Используется в качестве соглашения между фронтендом и бэкэндом.
Содержит в себе контроллер с методами для сохранения\изменения\удаления пользовательских пресетов.
Вставляем машруты в файл с роутами (там где необходимо).
Используется исключительно в админках, вместе с компонентом
PanelSet
...
Route::post('browser/preset/create', [BrowserFilterPresetController::class, 'create']);
Route::post('browser/preset/update', [BrowserFilterPresetController::class, 'update']);
Route::post('browser/preset/delete', [BrowserFilterPresetController::class, 'delete']);
...