iyoworks / former
[分支] Laravel 框架的强大表单生成器
This package is not auto-updated.
Last update: 2024-09-28 15:39:18 UTC
README
一种Laravel风格创建和格式化表单的方法
Former 是一个小项目,我想向大家介绍——它是一个针对 Laravel 框架的包,一旦你了解它,它也是一个非常好的伙伴。Laravel 的原始表单类很棒——简单且功能丰富,但与其他框架相比,略显不够优雅。当你在上面添加 Bootstrapper 类时,它就变得令人陶醉;创建带有控制组的五个字段表单所需的行数实在太多,让人望而却步。
Enter Former,一个强大的表单生成器,具有本地化、验证、重新填充和直接集成到 Bootstrap 和 Foundation 的辅助工具。
目录
介绍
Former 旨在通过将每个字段转换为其自身的模型,拥有自己的方法和属性,来重新实现 Laravel 的表单创建。这意味着你可以做这样的事情
Former::horizontal_open() ->id('MyForm') ->secure() ->rules(array( 'name' => 'required' )) ->method('GET') Former::xlarge_text('name') ->class('myclass') ->value('Joseph') ->required(); Former::textarea('comments') ->rows(10)->columns(20) ->autofocus(); Former::actions ( Former::large_primary_submit('Submit'), Former::large_inverse_reset('Reset') ) Former::close()
同时还能做——就像在那些日子里
Former::xlarge_text('name', null, 'Joseph', array('required' => true, 'class' => 'myclass')) Former::textarea('comments', null, null, array('rows' => 10, 'columns' => 20, 'autofocus' => true))
第一种方法的优势在于你可以省略参数。如果你想对一个文本字段设置一个类,你不必设置标签和值以及等等到 null,你只需这样做 Former::text('name')->class('class')。
每次调用一个实际不存在的函数时,Former 都假设你试图设置一个属性,并神奇地创建它。这就是为什么你可以在上面的例子中这样做 ->rows(10);如果你想设置包含破折号的属性,只需将它们替换为下划线:->data_foo('bar') 等于 data-foo="bar"。当然,如果你想设置包含下划线的属性(哎呀,你真是个小聪明),你始终可以使用回退方法 setAttribute('data_foo', 'bar')。祝你万事如意。
安装
安装Former非常简单。你只需在终端中键入以下内容
php artisan bundle:install former
将以下内容添加到你的 bundles.php 文件中
'former' => array('auto' => true),
最后,为了便于使用,我建议在 application.php 中的别名数组中添加此别名
'Former' => 'Former\Former',
特性
开箱即用的 Bootstrap 和 Foundation 集成
所以这非常不错,但到目前为止,这看起来只是 Laravel 表单类的修改,我的意思是,这一切有什么神奇之处呢?这就是隐藏在下面的魔法所在:Former 能够识别你创建的水平或垂直表单,并在幕后将每个字段包装在控制组中。这意味着当你键入以下内容时
Former::select('clients')->options($clients, 2) ->help('Pick some dude') ->state('warning')
你实际上得到以下输出(使用Bootstrap)
<div class="control-group warning"> <label for="clients" class="control-label">Clients</label> <div class="controls"> <select id="clients" name="clients"> <option value="0">Mickael</option> <option value="1">Joseph</option> <option value="2" selected="selected">Patrick</option> </select> <span class="help-inline">Pick some dude</span> </div> </div>
默认情况下,Former将使用Twitter Bootstrap进行语法设置,但您可以使用Former::framework()方法选择要使用的框架。目前Former支持'bootstrap'用于Twitter Bootstrap,'zurb'用于Zurb Foundation,以及null用于不使用框架。
// Turn off Bootstrap syntax Former::framework(null); // Turn it on again (MAKE UP YOUR MIND JEEZ) Former::framework('bootstrap');
以下是一个Foundation代码示例
Former::framework('zurb'); Former::four_text('foo')->state('error')->help('bar')
输出结果
<div class="error"> <label for="foo">Foo</label> <input class="four" type="text" name="foo" id="foo"> <small>Bar</small> </div>
与 Laravel 验证器集成
好吧,这样已经通过不需要调用冗长的Form::control_group()函数而移除了很多杂乱。现在我听到你的声音:“但你知道我仍然需要手动验证我的表单和……”——不,你不需要。进入Former的魔法助手withErrors;它的功能很简单。因为它已经将您美好的字段包裹到控制组中,所以它会继续前进,并轻轻检查该字段可能存在的任何错误,并将错误设置为.help-inline。现在我们谈论的是!
现在您将根据代码在验证失败后的反应以不同的方式使用它
如果您的验证失败后渲染视图(无重定向)
if($validation->fails()) { Former::withErrors($validation); return View::make('myview'); }
如果您的验证失败后进行重定向
if($validation->fails()) { return Redirect::to('login') ->with_errors($validation); }
现在,在最后一个示例中,您实际上从未在控制器或视图中调用Former。为什么是这样?那是因为当Former在页面上打开一个表单时,它会自动检查Session中是否存在名为errors的对象,如果存在,它将尝试使用它,而无需您调用任何东西。您可以使用以下选项禁用Former的自动错误检索:Former::config('fetch_errors', false)。
表单填充
您可以使用Former::populate函数轻松地用值填充表单。有两种方法可以做到这一点。第一种方法是传递一个包含值的数组,如下所示
// Will populate the field 'name' with the value 'value' Former::populate( array('name' => 'value') )
您也可以通过传递一个Eloquent模型来填充表单,例如,您有一个Client模型,您可以这样做。这允许您有很多好处,我稍后会回到这一点。
Former::populate( Client::find(2) )
Former将识别该模型,并使用模型的属性填充字段。例如,如果我们的客户有一个名为name的属性设置为'Foo',一个名为firstname的属性设置为'Bar',Former将查找名为'name'和'firstname'的字段,并分别用'Foo'和'Bar'填充它们。
或者,您也可以在填充整个表单之后(例如,对于关系)填充特定字段,方法如下
Former::populate($project) Former::populateField('client', $project->client->name)
对于表单的其余部分,填充字段基本上与使用->value('something')一样简单。要为<select>生成选项列表,您调用Former::select('foo')->options([array], [facultative: selected value])。您还可以使用Eloquent/Fluent查询的结果作为select的选项,如下所示
Former::select('foo')->fromQuery(Client::all(), 'name', 'id')
其中第二个参数是用于选项文本的属性,第三个可选参数是用于选项值的属性(默认为id属性)。Former还做一些魔法,如果这两个参数都没有指定。假设您向Former传递Eloquent模型,并且没有指定用作键或值的属性。Former将使用Eloquent的get_key()方法来获取键,并使用绑定到模型的任何__toString()方法作为原始值。以下是一个示例
class Client extends Eloquent { public static $key = 'code'; public function __toString() { return $this->name; } } Former::select('clients')->fromQuery(Client::all());
这与这样做相同,但是您知道,这更不痛苦,更DRY。这将使用每个Client的默认键,并将Client的名称作为选项的标签输出。
<div class="control-group"> <label for="foo" class="control-label">Foo</label> <div class="controls"> <select id="foo" name="foo"> @foreach(Client::all() as $client) @if(Input::get('foo', Input::old('foo')) == $client->code) <option selected="selected" value="{{ $client->code }}">{{ $client->name }}</option> @else <option value="{{ $client->code }}">{{ $client->name }}</option> @endif @endforeach </select> </div> </div>
Former还能够填充具有关系的字段。现在,一个例子胜过千言万语(除非,你知道,你的例子有一千个字长)
Former::populate(Client::find(2)) // Will populate with $client->name Former::text('name') // Will populate with $client->store->name Former::text('store.name') // You can go as deep as you need to Former::text('customer.name.adress') // Will populate with the date from all of the client's reservations Former::select('reservations.date') // Which is the same as this ^ Former::select('reservations')->fromQuery($client->reservations, 'date') // If you're using a text and not a select, instead of listing the // relationship's models as options, it wil concatenate them Former::text('customers.name') // Will display "name, name, name" // You can rename a field afterwards for easier Input handling Former::text('comment.title')->name('title')
向cviebrock致敬,他提出了原始想法。
数据列表
但它还能做什么?它还能做datalist。你不知道什么是datalist吗?好吧;你知道有时你希望人们在一个选择框中做出选择,但如果它不在其中,也能输入他们想要的?那就是datalist。在Former中,您可以简单地创建一个,如下所示
Former::text('clients')->useDatalist($clients) // Or use a Query object, same syntax than fromQuery() Former::text('projects')->useDatalist(Project::all(), 'name')
您还可以(如果需要的话)通过执行Former::text('foo')->list('myId')->useDatalist()来为创建的datalist设置自定义id。从那里开始,它将自动生成相应的<datalist>并将其通过id链接到该字段。这意味着您的文本输入将填充您数组中的值,同时仍然允许人们输入他们想要的任何内容,如果他们找不到快乐或有点烦人。
实时验证
更多。好吧,即时验证,我们都喜欢这个,不是吗?现在,正如你们中的一些人所知,所有现代浏览器都支持通过HTML属性进行即时验证——不需要JavaScript、脚本或polyfill。有几个属性可以为你做这种工作,比如pattern、required、max/min等。现在你知道当你使用那个小$rules数组验证你的POST数据时?不是很好奇能够直接将这个数组传递给表单,并让它将规则转换为真正的实时验证吗?是的?因为你可以使用Former做到这一点,只是随便说说。以下是一个(相当牵强的)规则数组
Former::open()->rules(array( 'name' => 'required|max:20|alpha', 'age' => 'between:18,24', 'email' => 'email', 'show' => 'in:batman,spiderman', 'random' => 'match:/[a-zA-Z]+/', 'birthday' => 'before:1968-12-03', 'avatar' => 'image', ));
Former会查找与键匹配的字段,并尽可能应用这些规则。目前支持的规则不多,但我计划添加更多。
<input name="name" type="text" required maxlength="20" pattern="[a-zA-Z]+" /> <input name="age" type="number" min="18" max="24" /> <input name="email" type="email" /> <input name="show" type="text" pattern="^(batman|spiderman)$" /> <input name="random" type="text" pattern="[a-zA-Z]+" /> <input name="birthday" type="date" max="1968-12-03" /> <input name="avatar" type="file" accept="image/jpeg,image/png,image/gif,image/bmp" />
请注意,您可以通过添加自定义规则的方式添加自定义规则,因为pattern属性使用正则表达式(如果您不熟悉正则表达式,您绝对应该学习,因为这将引导您通过生活或某种东西)。
Former::number('age')->min(18) Former::text('client_code')->pattern('[a-z]{4}[0-9]{2}')
就是这样!而且最好的消息是:由于Bootstrap识别实时验证,如果您尝试在姓名字段中输入不符合alpha模式的内容,它将自动变成红色,就像当您的控制组设置为error时一样。就是这样,手指拍打,什么也不做,只是坐着,放松,看着Chrome/Firefox/什么弹出一个小巧玲珑的框说“你必须填写那个字段,伙计”或“那不是电子邮件,你在尝试做什么,愚弄我吗?”
您还可以中途手动设置控制组的状态——当然,这是仅在您使用Bootstrap或Foundation的情况下才可用的功能。您可以使用任何控制组状态,包括success、warning、error和info。
Former::text('name')->state('error')
文件处理
在Former中,就像在Laravel中一样,您可以使用Former::file创建一个简单的文件字段。新的功能是,您还可以通过调用Former::files创建多个文件字段,这将生成<input type="file" name="foo[]" multiple />。
有一个特殊的方法是->accept(),您可以使用它来做到以下几点
// Use a shortcut (image, video or audio) Former::files('avatar')->accept('image') // Use an extension which will be converted to MIME by Laravel Former::files('avatar')->accept('gif', 'jpg') // Or directly use a MIME Former::files('avatar')->accept('image/jpeg', 'image/png')
您也可以通过使用比特或字节轻松设置最大大小
Former::file('foo')->max(2, 'MB') Former::file('foo')->max(400, 'Ko') Former::file('foo')->max(1, 'TB')
这将创建一个隐藏的MAX_FILE_SIZE字段,并具有正确的字节数值。
复选框和单选按钮
复选框和单选按钮,伙计,不是令人讨厌的吗?尤其是当你必须创建几个时,你会在脑海中想,“为什么我不能验证所有这些?”使用Former,这要简单得多
// Create a one-off checkbox Former::checkbox('checkme') // Create a one-off checkbox with a text, and check it Former::checkbox('checkme') ->text('YO CHECK THIS OUT') ->check() // Create four related checkboxes Former::checkboxes('checkme') ->checkboxes('first', 'second', 'third', 'fourth') // Create related checkboxes, and inline them Former::checkboxes('checkme') ->checkboxes($checkboxes)->inline() // Everything that works on a checkbox also works on a radio element Former::radios('radio') ->radios(array('label' => 'name', 'label' => 'name')) ->stacked() // Stacked and inline can also be called as magic methods Former::inline_checkboxes('foo')->checkboxes('foo', 'bar') Former::stacked_radios('foo')->radios('foo', 'bar') // Set which checkables are checked or not in one move Former::checkboxes('level') ->checkboxes(0, 1, 2) ->check(array('level_0' => true, 'level_1' => false, 'level_2' => true)) // Fine tune checkable elements Former::radios('radio') ->radios(array( 'label' => array('name' => 'foo', 'value' => 'bar', 'data-foo' => 'bar'), 'label' => array('name' => 'foo', 'value' => 'bar', 'data-foo' => 'bar'), ))
重要提示:Former为您提供了一个选项来强制复选框的推送。这是什么意思?这意味着当复选框未选中时,它们仍然会在您的POST数据中弹出。这听起来很正常,但实际上与表单的怪异默认行为相反。“它是未选中的?我完全不记得该字段曾经存在过”。您可以通过unchecked_value选项更改未选中复选框在POST数组中的值。
通过复选框/单选按钮()方法创建可检查项时,默认情况下,对于每个可检查项的name属性,它将使用您指定的原始名称并附加一个数字(在这里,在我们的例子中将是<input type="checkbox" name="checkme_2">)。它还会重新填充它,这意味着已选中的输入将在提交时保持选中状态。
本地化辅助工具
对于那些从事多语言项目的你们,Former也在这里提供帮助。默认情况下,在创建字段时,如果没有指定标签,Former将默认使用字段名称。但更重要的是,它将尝试自动翻译。同样的规则也适用于复选框标签、帮助文本和表单标题。这意味着以下内容
// This Former::label(__('validation.attributes.name')) Former::text('name', __('validation.attributes.name')) Former::text('name')->inlineHelp(__('help.name')) Former::checkbox('rules')->text(__('my.translation')) <legend>{{ __('validation.attributes.mylegend') }}</legend> // Is the same as this Former::label('name') Former::text('name') Former::text('name')->inlineHelp('help.name') Former::checkbox('rules')->text('my.translation') Former::legend('mylegend')
这听起来可能很酷。Former首先尝试翻译字符串本身,即my.text将返回__('my.text'),如果失败,它将在备用位置查找。您可以通过更改以下变量来设置Former查找翻译的位置:Former::config('translate_from', [boolean])(默认为validation.attributes)。请注意,它必须是一个数组。
设置字段值的注意事项
所有表单类在某个时候都会遇到一个问题:哪种类型的数据比哪种数据更重要?为了填充您的字段,Former设置了以下优先级以找到值
- POST数据具有最高优先级——如果用户在字段中输入了某些内容,那么很可能这是他们下次想看到的内容
- 然后是使用
->forceValue()方法设置的数据——它是为了在字段中强制设置值而创建的->value()的特殊分支 - 然后是任何通过
Former::populate()设置的数据——这意味着如果您正在编辑某些内容或用某些值重新填充,它可以用forceValue覆盖 - 最后,经典的
->value()具有最低优先级——它是为了创建最小和默认字段值而创建的,因此会被填充数据和POST数据覆盖
最终对决
// Laravel <div class="control-group"> {{ Form::label('input01', __('validation.attributes.input01'), array('class' => 'control-label') )}} <div class="controls"> <div class="input-prepend input-append"> <span class="add-on">@</span> {{ Form::text('input01', 'myname', (Input::get('input01', Input::old('input01')), array('class' => 'input-xlarge')) }} <span class="add-on"><i class="icon-white icon-enveloppe"></i></span> </div> <p class="help-block">This is an help text</p> </div> </div> // Bootstrapper echo Form::prepend_append( Form::control_group( Form::label('input01', __('validation.attributes.input01')), Form::xlarge_text('input01'), (Input::get('input01', Input::old('input01', 'myname'))), $validation->errors->get('input01'), Form::block_help('This is an help text') ), '@', Icon::white_enveloppe() ); // Former Former::xlarge_text('input01', 'Text input') ->blockHelp('This is an help text') ->prepend('@')->appendIcon('white-enveloppe') ->value('myname')
// Laravel // Man don't even get me started // Boostrapper echo Form::control_group( Form::label('checkboxes', 'Check those boxes'), Form::inline_labelled_checkbox('check1', 'Check me', 1, Input::get('check1', Input::old('check1'))). Form::inline_labelled_checkbox('check2', 'Check me too', 1, Input::get('check2', Input::old('check2'))), $validation->errors->get('check1'), Form::inline_help('I SAID CHECK THOSE DOUBLES') ); // Former Former::checkboxes('check') ->checkboxes('Check me', 'Check me too') ->help('I SAID CHECK THOSE DOUBLES')
侧边栏
这里可能看起来我似乎在贬低Laravel和Bootstrapper,但请耐心一点——我根本不是这个意思。我爱Laravel,它是一个惊人而优雅的框架,我无法强调每个人应该在自己的包中安装Bootstrapper——甚至我还在积极合作该项目。我甚至打算用Former取代Bootstrapper的表单类,但对我来说,这超出了它的范围。
不管怎样,现在就到这里,希望你们喜欢,别忘了在问题中报告任何问题/错误/想法/问题。