dg482/red

自动组装和配置资源管理界面的用户界面

dev-main 2021-05-30 07:20 UTC

This package is auto-updated.

Last update: 2024-09-15 10:40:39 UTC


README

Build Status codecov

此包旨在确定资源、构建表格、表单和导航菜单。

该包 不是一个独立的构建管理部分的解决方案。它旨在与框架集成,如 Laravel 和 Yii。

确定使用的实体

资源

资源旨在执行与指定模型的基本操作 CRUD。在初始化资源时,定义数据库适配器,实现 \Dg482\Red\Adapters\Interfaces\AdapterInterfaces,通过它获取模型表格的字段列表并初始化资源字段。

通过适配器调用 Create、Read、Udate、Delete 命令。命令接口的扩展是通过执行操作实现的,每个操作都可以定义适配器的命令。

通过适配器命令执行数据库查询的方案

资源应包含模型定义、输入数据验证规则、考虑本地化的字段名称、控制菜单项视觉表现的参数。

Resource::initResource() 方法中定义基本参数

  • Resource::setTitle() - 菜单点标题,资源元素表中的标题
  • Resource::setIcon() - 菜单点的图标
  • Resource::setLabels() - 表格和表单字段的标签,如果未设置标签,则使用数据库列的 注释
  • Resource::setHiddenFields() - 定义隐藏字段(注意! 字段将在构建表格或表单时被忽略,要为字段指定 hidden 类型,需要在资源表单中重定义字段)
  • Resource::setAssets() - 方法定义文件存储库,实现接口 ResourceAssetsInterface 以处理附加到资源模型的媒体材料(图像、文档文件等)。

方法 Resource::getFormModel() 应返回扩展基本实现 Dg482\Red\Builders\Form\BaseForms 的模型表单

WebinarResource 资源
namespace App\Resources\Webinar;

use App\Models\Webinar;
use App\Resources\Webinar\Forms\WebinarItemForm;
use Dg482\Red\Builders\Form\BaseForms;
use Dg482\Red\Resource\Resource;

/**
 * Class WebinarResource
 * @package App\Resources\Webinars
 */
class WebinarResource extends Resource
{
    /** 
     * Определение модели
     * @var string 
     */
    protected string $resourceModel = Webinar::class;

    /**
     * Метод инициализации параметров ресурса
     * 
     * @param  string  $context
     * @return Resource
     */
    public function initResource(string $context = ''): Resource
    {
        // определение параметров отображения
        $this->setIcon('video-camera') // иконка в пункте меню
            ->setTitle('Вебинары')// название пункта меню
            ->setLabels([// подписи полей формы, колонок в таблице
                'title' => 'Название',
                'url' => 'Ссылка на Вебинар',
            ])
            ->setHiddenFields([// скрытые поля
                'deleted_at',
            ])
            ->setContext(__CLASS__);
            
        // определение правил проверки ввода с учетом интеграции с фреимворком, в данном случае правила для Laravel
        $this->validators = [
            'title' => ['required', 'max:80'],
        ];

        $this->setAssets(new WebinarStorage);
        
        return $this;
    }

    /**
     * Метод возвращает форму ресурса в которой определены вспомогательные методы для работы с полями
     *
     * @return BaseForms
     */
    public function getFormModel(): BaseForms
    {
        if (!$this->formModel) {
            $this->setForm(new WebinarItemForm(new $this->resourceModel));
        }

        return parent::getFormModel();
    }
}
WebinarResource 资源的 WebinarStorage 存储(Laravel)
namespace App\Resources\Webinar\Assets;

use App\Models\Files\Storage;
use Dg482\Red\Interfaces\ResourceAssetsInterface;

/**
 * Class WebinarStorage
 * 
 * В контексте данного примера класс WebinarStorage расширяет Storage
 * который является типовой реализацией работы с моделью БД для регистрации файлов. 
 * От проекта к проекту данная реализация может отличаться поэтому 
 * в качестве примера рассматриваются только методы интерфейса ResourceAssetsInterface.
 * 
 * @package App\Resources\Webinar\Assets
 */
class WebinarStorage extends Storage implements ResourceAssetsInterface
{
    /**
     * Удаление файла
     *
     * @return bool
     */
    public function remove(): bool
    {
        \Illuminate\Support\Facades\Storage::disk('public')->delete($this->{self::PATH});

        return $this->delete();
    }

    /**
     * Получение модели по идентификатору привязки к файлу
     * @param  int  $id
     * @return ResourceAssetsInterface
     */
    public function get(int $id): ResourceAssetsInterface
    {
        $item = $this->where([
            Storage::OWNER_TYPE => Storage::TYPE_CATALOG,
            'id' => $id,
        ])->first();

        return $item ?? new self();
    }

    /**
     * Сохранение привязки загруженного файла
     *
     * @param  array  $parameter
     * @return bool
     */
    public function store(array $parameter): bool
    {
        $item = self::create([
            Storage::OWNER_TYPE => Storage::TYPE_CATALOG,
            Storage::OWNER_ID => $parameter[Storage::OWNER_ID],
            Storage::PATH => $parameter[Storage::PATH],
            Storage::STORAGE => Storage::STORAGE_LOCAL,
            Storage::FILE => $parameter[Storage::FILE] ?? $parameter[Storage::PATH],
        ]);

        return $item->id > 0 ?? false;
    }
}
WebinarResource 资源的 WebinarItemForm 表单(Laravel)
namespace App\Resources\Webinar\Forms;

use App\Models\Webinar;
use Dg482\Red\Builders\Form\BaseForms;
use Dg482\Red\Builders\Form\Fields\Field;
use Dg482\Red\Builders\Form\Fields\HiddenField;
use Dg482\Red\Builders\Form\Fields\DateField;
use Dg482\Red\Exceptions\EmptyFieldNameException;
use Illuminate\Database\Eloquent\Builder;
use Carbon\Carbon;

/**
 * Определение формы для работы с моделью Webinar, содержит методы модификации полей по умолчанию
 *
 * @package App\Resources\Webinars\Forms
 */
class WebinarItemForm extends BaseForms
{
    /**
     * Category constructor.
     * @param  Webinar  $model
     */
     public function __construct(Webinar $model)
     {
        $this->setTitle('Вебинар');// заголовок формы
        $this->setFormName('webinar/item'); // идентификатор формы
        $this->setModel($model); // определение модели в контексте формы
     }
     
     
    /**
     * Переопределение поля Дата создания
     * 
     * При переопределение происходит корректировка отображения поля в форме 
     * и определение замыкания вызываемого при построение запроса из таблицы ресурса определяющего
     * условие фильтрации по полю.
     * 
     * @param  Field  $field
     * @return Field
     * @throws EmptyFieldNameException
     */
    public function formFieldCreatedAt(Field $field): Field
    {
        return (new DateField)
            ->setFilterFn(function (Builder &$builder, array $request) use ($field) {// замыкание для условия запроса к БД
                if (!empty($request['created_at'])) {
                    if (count($request['created_at']) === 1) {// если передано одно значение
                        $date = Carbon::createFromFormat('Y-m-d', current($request['created_at']));
                        // фильтруем в диапазоне начала и конца даты
                        $builder->whereBetween('created_at', [
                            $date->startOfDay()->format(Carbon::DEFAULT_TO_STRING_FORMAT),
                            $date->endOfDay()->format(Carbon::DEFAULT_TO_STRING_FORMAT),
                        ]);
                    } elseif (!empty($request['created_at'][0]) && !empty($request['created_at'][1])) {
                        $start = Carbon::createFromFormat('Y-m-d', $request['created_at'][0]);
                        $end = Carbon::createFromFormat('Y-m-d', $request['created_at'][1]);
                        // фильтруем в диапазоне начала и конца дат
                        $builder->whereBetween('created_at', [
                            $start->startOfDay()->format(Carbon::DEFAULT_TO_STRING_FORMAT),
                            $end->endOfDay()->format(Carbon::DEFAULT_TO_STRING_FORMAT),
                        ]);
                    }
                }
            })
            ->setFilterMultiple()// возможность выбора диапазона значений
            ->hideForm() // скрываем отображение в форме
            ->setField($field->getField()) // определение поля
            ->setName($field->getName())// определение названия
            ->setValue($field->getValue()->getValue());
    }
    
    /**
     * Переопределение поля url
     *
     * @param  Field  $field
     * @return Field
     */
     public function formFieldUrl(Field $field): Field
     {
        return $field->hideTable();// скрыть поле в таблице
     }

    /**
     * Переопределение типа поля id
     * @param  Field  $field
     * @return Field
     * @throws \Dg482\Red\Exceptions\EmptyFieldNameException
     */
     public function formFieldId(Field $field): Field
     {
        return (new HiddenField)
                ->setField($field->getField())
                ->setValue($field->getValue()->getValue())
                ->hideTable();
     }
}

还可以将资源用作静态的任意接口生成器。