ghebby/laravel-hfm

一个旨在通过在运行时从模型中的简单数组声明生成代码来减少应用程序中冗长和重复代码的 Laravel 包。

v1.0.0 2021-01-17 17:22 UTC

This package is auto-updated.

Last update: 2024-09-18 01:33:29 UTC


README

不稳定

一个简单的 Laravel 包,用于帮助您实现 DRY(不要重复自己)原则

Latest Version on Packagist GitHub Tests Action Status Total Downloads

此包旨在通过从模型中的简单数组声明开始,在运行时生成代码来减少应用程序中的冗长和重复代码。

这是一个有偏见的方案,所以在使用之前请确保它符合您的项目指南。

安装

  1. 此包发布了一个 config/laravel-hfm.php 配置文件。如果您已经有了同名文件,您必须重命名或删除它。
  2. 您可以通过 composer 安装此包
composer require ghebby/laravel-hfm
  1. 可选:服务提供程序将自动注册。或者您也可以手动在您的 config/app.php 文件中添加服务提供程序
'providers' => [
    // ...
    Ghebby\LaravelHfm\LaravelHfmServiceProvider::class,
];
  1. 您应该使用以下命令发布 config/laravel-hfm.php 配置文件:
php artisan vendor:publish --provider="Ghebby\LaravelHfm\LaravelHfmServiceProvider" --tag="config"
  1. 如果您想自定义 UI 辅助函数的视图,则运行以下命令以将其发布到 resources/views/vendor/laravel-hfm/
php artisan vendor:publish --provider="Ghebby\LaravelHfm\LaravelHfmServiceProvider" --tag="views"

来发布它们

用法

在模型上定义字段映射可以在代码重复方面带来很多好处,使开发应用程序的简单部分更容易、更快。

让我们看一个简短的例子。 模型名称:company 模型字段

  • id
  • name
  • fiscal_code
  • email
  • phone
  • description
  • address
  • city
  • zip
  • country

典型方法

在典型的模型 Company.php 中,我们会有这样的东西

namespace App\Models;

class Company extends Model
{
    protected $table = 'companies';

    protected $fillable = ['name', 'fiscal_code', '...'];
    // OR
    protected $guarded = ['id'];

}

在 CompanyController 中,我们会有类似的东西

namespace App\Http\Controllers;

class CompanyController extends Controller
{

    // [...]

    public function create(Request $request)
    {
        // 
        return view('company.create');
    }

    public function store(Request $request)
    {
        // before persisting the data n DB you must validate it.
        // thus writing validation rules is a mandatory action
        // and it might look like this.
        $validaData = $request->validate(
            [
                'name' => 'string|max:255|required',
                'fiscal_code' => 'string|max:255|required',
                'email' => 'email|max:255|required',
                'phone' => 'string|max:255|nullable',
                //....
            ]
        ); 

        Company::create($validaData);

        return redirect()->route('company.index');
    }

    public function edit(Request $request, $id)
    {
        //
        $company = Company::findOrFail($id);

        return view('company.edit')->with('company', $company);
    }

    public function update(Request $request, $id)
    {
        // before persisting the data n DB you must validate it.
        // thus writing validation rules is a mandatory action
        // and it might look like this.
        $validaData = $request->validate(
            [
                'name' => 'string|max:255|required',
                'fiscal_code' => 'string|max:255|required',
                'email' => 'email|max:255|required',
                'phone' => 'string|max:255|nullable',
                //....
            ]
        ); 

        $company = Company::findOrFail($id);
        $company->update($validaData);

        return redirect()->route('company.index');
    }

    // [...]
}

在视图方面,一个典型的情况可能是

//[....]
<form method="POST" action="{ route('company.store') }" >
    @csrf
    <div>
        <div>
            <label for="name">Name</label>
            <input name="name" type="text" class="" required>
        </div>
        <div>
            <label for="fiscal_code">Fiscal Code</label>
            <input name="fiscal_code" type="text" class="" required>
        </div>
        // [...]
        <div>
            <label for="country">Country</label>
            <input name="country" type="text" class="">
        </div>
    </div>
    <button type="submit">Create</button>
</form>
//[....]

希望您能轻松地看到问题。如果出于某种原因约束发生变化,我们将在几个地方修复代码

  • 在控制器@store -> validationRules
  • 在控制器@update -> validationRules
  • 在视图.create -> fields
  • 在视图.edit -> fields

使用此包

此包提供

  • 一个要由模型实现的 Contract 接口
  • 一个 HfmTrait,其中包含用于操作字段映射的最常用方法
  • 一个 HfmConst 和 HfmHelper,在应用程序启动时自动加载,以便定义常量
  • 一些视图辅助函数,具有根据字段映射生成 UI 的能力

让我们看看使用此包时代码如何改变

模型 Company.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use Ghebby\LaravelHfm\Traits\FieldsMapTrait;
use Ghebby\LaravelHfm\Contracts\FieldsMapContract;

class Company extends Model implements FieldsMapContract
{
    use FieldsMapTrait;

    protected $table = 'companies';

    protected $fillable = ['name', 'fiscal_code', '...'];
    // OR
    protected $guarded = ['id'];

    /**
     * Returns the DB fields for the model. 
     * This map is used to automatically the create/update form
     */
    public static function getFieldsMap() {

        $fields = [
            'id' => [ FLD_LABEL => 'Id', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_INT, FLD_LENGTH => 11 , FLD_FLT_COND => 'LIKE', FLD_PRIMARY => true,  FLD_REQUIRED => false ] ,
            'name' => [ FLD_LABEL => 'Company name', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 255 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ] ,
            'fiscal_code' => [ FLD_LABEL => 'Fiscal Code', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 255 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ] ,
            'email' => [ FLD_LABEL => 'E-mail', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 255 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ] ,
            'phone' => [ FLD_LABEL => 'Phone', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 50 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => false ],
            'description'=> [ FLD_LABEL => 'Description',FLD_UI_CMP => CMP_TEXT_AREA,  FLD_DATA_TYPE => DT_TEXT_AREA, FLD_LENGTH => -1, FLD_FLT_COND => 'LIKE', FLD_REQUIRED => false ] ,
            'address' => [ FLD_LABEL => 'Address', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 255 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ],
            'city' => [ FLD_LABEL => 'City', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 100 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ],
            'zip' => [ FLD_LABEL => 'zip', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 10 , FLD_FLT_COND => '=', FLD_REQUIRED => true ],
            'country' => [ FLD_LABEL => 'Country', FLD_UI_CMP => CMP_TEXT,  FLD_DATA_TYPE => DT_TEXT, FLD_LENGTH => 100 , FLD_FLT_COND => 'LIKE', FLD_REQUIRED => true ],
        ];

        return $fields;
    }

}

控制器上

namespace App\Http\Controllers;

class CompanyController extends Controller
{

    // [...]

    public function create(Request $request)
    {
        // 
        $fields = Company::getFieldsMap();
        
        if( isset($fields['id']) ){
            // when creating a new entry, id field in required normaly.
            unset($fields['id']);
        }

        return view('company.create')->with('fields', $fields);
    }

    public function store(Request $request)
    {

        $rules = Company::getDefaultValidationRules();
        // $rules is an array of validation rules for each field in the field map, 
        // which nakes it very easy to override or integrate rules, example
        // $rules['fiscal_code'][] = 'min:10'; // addd a constraint for min number of char
        // unset($rules['email']); // this will prevent the validator method to set the key value pair for 'email' in $validaData

        $validaData = $request->validate($rules); 

        Company::create($validaData);

        return redirect()->route('company.index');
    }

    public function edit(Request $request, $id)
    {
        //
        $company = Company::findOrFail($id);
        $fields = Company::getFieldsMap();

        return view('company.edit')->with('company', $company)->with('fields', $fields);
    }

    public function update(Request $request, $id)
    {

        $rules = Company::getDefaultValidationRules();
        $validaData = $request->validate($rules);

        $company = Company::findOrFail($id);
        $company->update($validaData);

        return redirect()->route('company.index');
    }

    // [...]
}

视图方面

//[....]
<form method="POST" action="{ route('company.store') }" >
    @csrf
    <div>
        @if( count($fileds) > 0 )
            // if you have published and customized the UI helper functions, use this
            @include('vendor.anatolieghebea.laravel-hfm.helpers._standardForm', ['stdFields' => $fileds])

            // if using the package default helper functions
            @include('laravel-hfm::helpers._standardForm', ['stdFields' => $fileds])
        @endif
    </div>
    <button type="submit">Create</button>
</form>
//[....]
//[....]
<form method="POST" action="{ route('company.edit', $company->id) }" >
    @csrf
    <div>
        @if( count($fileds) > 0 )
            // if you have published and customized the UI helper functions, use this
            @include('vendor.anatolieghebea.laravel-hfm.helpers._standardForm', ['stdFields' => $fileds, 'mainModel' => $company, 'op' => 'edit'])

            // if using the package default helper functions
            @include('laravel-hfm::helpers._standardForm', ['stdFields' => $fileds, 'mainModel' => $company, 'op' => 'edit'])
        @endif
    </div>
    <button type="submit">Save</button>
</form>
//[....]

使用这种方法,我们将应用程序中的真理来源集中在一起,如果我们更改 Company.php 中字段的标签 name,它将传播到所有依赖于 _standardForm 辅助函数的视图。

如果字段映射中 fiscal_code 的属性 required 设置为 FALSE,则更改将立即影响控制器中的存储和更新方法,以及输入字段的 required 属性。

添加或删除字段

向模型实体添加或删除字段只是添加或删除模型 getFieldMap() 中的行的问题。

测试

composer test

变更日志

有关最近更改的更多信息,请参阅 CHANGELOG

贡献

有关详细信息,请参阅 CONTRIBUTING

安全漏洞

请审查我们的安全策略,了解如何报告安全漏洞。

致谢

许可证

MIT 许可证(MIT)。有关更多信息,请参阅许可证文件