iyoworks/former

此包的最新版本(2.6.0)没有可用的许可信息。

[分支] Laravel 框架的强大表单生成器

2.6.0 2012-12-05 08:58 UTC

This package is not auto-updated.

Last update: 2024-09-28 15:39:18 UTC


README

一种Laravel风格创建和格式化表单的方法

Travis状态:Build Status

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。有几个属性可以为你做这种工作,比如patternrequiredmax/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的情况下才可用的功能。您可以使用任何控制组状态,包括successwarningerrorinfo

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的表单类,但对我来说,这超出了它的范围。

不管怎样,现在就到这里,希望你们喜欢,别忘了在问题中报告任何问题/错误/想法/问题。