refactor OptonForm

This commit is contained in:
printempw 2016-12-28 23:28:15 +08:00
parent da7a0ac606
commit 1d7dc13e18
7 changed files with 206 additions and 237 deletions

View File

@ -84,29 +84,24 @@ class AdminController extends Controller
$form->text('site_description', '站点描述');
$form->text('site_url', '站点地址URL')->hint('以 http(s):// 开头,不要以 / 结尾');
$form->checkbox('user_can_register', '开放注册', '任何人都可以注册');
$form->checkbox('user_can_register', '开放注册')->label('任何人都可以注册');
$form->text('regs_per_ip', '每个 IP 限制注册数');
$form->group('max_upload_file_size', '最大允许上传大小', function($group) {
// main textbox
$group->text('max_upload_file_size');
$group->addon('KB');
})->hint('PHP 限制:'.ini_get('post_max_size').',定义在 php.ini 中。');
$form->group('max_upload_file_size', '最大允许上传大小')
->text('max_upload_file_size')->addon('KB')
->hint('PHP 限制:'.ini_get('post_max_size').',定义在 php.ini 中。');
$form->checkbox('allow_chinese_playername', '角色名', '允许中文角色名');
$form->checkbox('allow_chinese_playername', '角色名')->label('允许中文角色名');
$form->select('api_type', '首选 JSON API', function($options) {
$options->add('0', 'CustomSkinLoader API');
$options->add('1', 'UniversalSkinAPI');
});
$form->select('api_type', '首选 JSON API')
->option('0', 'CustomSkinLoader API')
->option('1', 'UniversalSkinAPI');
$form->checkbox('auto_del_invalid_texture', '失效材质', '自动删除失效材质')->hint('自动从皮肤库中删除文件不存在的材质记录');
$form->checkbox('auto_del_invalid_texture', '失效材质')->label('自动删除失效材质')->hint('自动从皮肤库中删除文件不存在的材质记录');
$form->textarea('comment_script', '评论代码')->rows(6)->description('评论代码内可使用占位符,<code>{tid}</code> 将会被自动替换为材质的 id<code>{name}</code> 会被替换为材质名称,<code>{url}</code> 会被替换为当前页面地址。');
$form->textarea('comment_script', '评论代码', function($textarea) {
$textarea->setRows(6);
$textarea->setDescription('评论代码内可使用占位符,<code>{tid}</code> 将会被自动替换为材质的 id<code>{name}</code> 会被替换为材质名称,<code>{url}</code> 会被替换为当前页面地址。');
});
})->handle(function() {
if (substr($_POST['site_url'], -1) == "/")
$_POST['site_url'] = substr($_POST['site_url'], 0, -1);
@ -114,13 +109,13 @@ class AdminController extends Controller
$cache = Option::form('cache', '资源文件配置', function($form)
{
$form->checkbox('force_ssl', '强制 SSL', '强制使用 HTTPS 协议加载资源')->hint('请确认 SSL 可用后再开启');
$form->checkbox('auto_detect_asset_url', '资源地址', '自动判断资源文件地址')->hint('根据当前 URL 自动加载资源文件,如果关闭则将根据「站点地址」填写的内容加载。如果出现 CDN 回源问题请关闭');
$form->checkbox('return_200_when_notfound', 'HTTP 响应码', '请求不存在的角色时返回 200 而不是 404');
$form->checkbox('force_ssl', '强制 SSL')->label('强制使用 HTTPS 协议加载资源')->hint('请确认 SSL 可用后再开启');
$form->checkbox('auto_detect_asset_url', '资源地址')->label('自动判断资源文件地址')->hint('根据当前 URL 自动加载资源文件,如果关闭则将根据「站点地址」填写的内容加载。如果出现 CDN 回源问题请关闭');
$form->checkbox('return_200_when_notfound', 'HTTP 响应码')->label('请求不存在的角色时返回 200 而不是 404');
$form->text('cache_expire_time', '缓存失效时间')->hint('秒数86400 = 一天31536000 = 一年');
})->hint('如果启用了 CDN 缓存请适当修改这些配置')->handle();
})->type('warning')->hint('如果启用了 CDN 缓存请适当修改这些配置')->handle();
return view('admin.options')->with('forms', compact('general', 'cache'));
}

View File

@ -12,9 +12,10 @@ class OptionForm
public $title;
protected $hint;
protected $type = 'primary';
protected $items;
protected $success = false;
protected $values;
protected $messages = [];
@ -26,66 +27,27 @@ class OptionForm
$this->title = $title;
}
public function text($id, $name, $value = null)
public function __call($name, $arguments)
{
return $this->addItem('text', $id, $name)->set('value', $value);
}
public function checkbox($id, $name, $label, $checked = null)
{
$checkbox = $this->addItem('checkbox', $id, $name)->set('label', $label);
return $checkbox;
}
public function select($id, $name, $callback)
{
$item = $this->addItem('select', $id, $name);
$select = new OptionFormSelect($id);
call_user_func($callback, $select);
$item->set('view', $select);
return $select;
}
public function textarea($id, $name, $callback)
{
$item = $this->addItem('textarea', $id, $name);
$textarea = new OptionFormTextarea($id);
call_user_func($callback, $textarea);
$item->set('view', $textarea);
return $textarea;
}
public function group($id, $name, $callback)
{
$item = $this->addItem('group', $id, $name);
$group = new OptionFormGroup($id);
call_user_func($callback, $group);
$item->set('view', $group);
return $item;
}
public function addItem($type, $id, $name)
{
$item = new OptionFormItem($id, $name, $type);
if (!in_array($name, ['text', 'checkbox', 'textarea', 'select', 'group'])) {
throw new \InvalidArgumentException("No such item for option form.", 1);
}
$class = new \ReflectionClass('App\Services\OptionForm'.Str::title($name));
// use ReflectionClass to create a new OptionFormItem instance
$item = $class->newInstanceArgs($arguments);
$this->items[] = $item;
return $item;
}
public function type($type)
{
$this->type = $type;
return $this;
}
public function hint($hint_content)
{
$this->hint = view('vendor.option-form.hint')->with('hint', $hint_content)->render();
@ -93,6 +55,13 @@ class OptionForm
return $this;
}
public function setValues(array $values)
{
$this->values = array_merge($this->values, $values);
return $this;
}
public function addMessage($msg, $type = "info")
{
$this->messages[] = "<div class='callout callout-$type'>$msg</div>";
@ -105,21 +74,31 @@ class OptionForm
call_user_func($callback, $this);
}
$arrayOptionCache = [];
foreach ($this->items as $item) {
if ($item->type == "checkbox" && !isset($_POST[$item->id])) {
if ($item instanceof OptionFormCheckbox && !isset($_POST[$item->id])) {
// preset value for checkboxes which are not checked
$_POST[$item->id] = "0";
}
if (Str::is('*[*]', $item->id))
// Str::is('*[*]', $item->id)
if (false !== ($result = $this->parseIdWithOffset($item->id))) {
// push array option value to cache
$arrayOptionCache[$result['id']][$result['offset']] = $_POST[$item->id];
continue;
}
if ($_POST[$item->id] != option($item->id, null, false)) {
Option::set($item->id, $_POST[$item->id]);
}
}
session()->flash($this->id.'.status', 'success');
foreach ($arrayOptionCache as $key => $value) {
Option::set($key, serialize($value));
}
$this->addMessage('设置已保存。', 'success');
}
return $this;
@ -132,48 +111,64 @@ class OptionForm
return $this;
}
protected function parseIdWithOffset($id)
{
// detect if id is formatted as *[*]
// array option is stored as unserialized string
preg_match('/(.*)\[(.*)\]/', $id, $matches);
if (isset($matches[2])) {
return [
'id' => $matches[1],
'offset' => $matches[2]
];
}
return false;
}
/**
* Load value from $this->values & options.
*
* @param string $id
* @return mixed
*/
protected function loadValueFromId($id)
{
if (false === ($result = $this->parseIdWithOffset($id))) {
return option($id);
} else {
$option = Arr::get(
$this->values,
$result['id'],
// fallback to load from options
@unserialize(option($result['id']))
);
return Arr::get($option, $result['offset']);
}
}
public function render()
{
if (!is_null($this->alwaysCallback)) {
call_user_func($this->alwaysCallback, $this);
}
// load values for items if not set manually
foreach ($this->items as $item) {
$id = $item->id;
if (!$value = $item->get('value')) {
preg_match('/(.*)\[(.*)\]/', $id, $matches);
if (isset($matches[2])) {
$option = @unserialize(option($matches[1]));
$value = Arr::get($option, $matches[2]);
} else {
$value = option($item->id);
if ($item instanceof OptionFormGroup) {
foreach ($item->items as $groupItem) {
if ($groupItem['id'] && is_null($groupItem['value'])) {
$groupItem['value'] = $this->loadValueFromId($groupItem['id']);
}
}
continue;
}
switch ($item->type) {
case 'text':
$view = view('vendor.option-form.text')->with(compact('id', 'value'));
break;
case 'checkbox':
$view = view('vendor.option-form.checkbox')->with([
'id' => $id,
'label' => $item->get('label'),
'checked' => (bool) $value
]);
break;
case 'select':
case 'textarea':
case 'group':
$view = $item->get('view')->render();
break;
if (is_null($item->value)) {
$item->value = $this->loadValueFromId($item->id);
}
$item->setContent($view->render());
}
return view('vendor.option-form.main')->with(get_object_vars($this))->render();
@ -183,159 +178,139 @@ class OptionForm
class OptionFormItem
{
public $id;
public $type;
public $title;
protected $data;
public $name;
protected $hint;
protected $content;
public $value = null;
public function __construct($id, $title, $type = "")
public $hint;
public $description;
public function __construct($id, $name)
{
$this->id = $id;
$this->type = $type;
$this->title = $title;
$this->id = $id;
$this->name = $name;
}
public function hint($hint_content)
public function value($value)
{
$this->hint = view('vendor.option-form.hint')->with('hint', $hint_content)->render();
$this->value = $value;
return $this;
}
public function setContent($content)
public function hint($hintContent)
{
$this->content = $content;
$this->hint = view('vendor.option-form.hint')->with('hint', $hintContent)->render();
return $this;
}
public function set($key, $value)
{
$this->data[$key] = $value;
return $this;
}
public function get($key)
{
return Arr::get($this->data, $key);
}
public function render()
{
return view('vendor.option-form.item')->with([
'title' => $this->title,
'content' => $this->content,
'hint' => $this->hint
]);
}
}
class OptionFormSelect
{
protected $id;
protected $items;
protected $selected = null;
public function __construct($id)
{
$this->id = $id;
}
public function add($id, $name)
{
$this->items[] = [$id, $name];
}
public function setSelected($id)
{
$this->selected = $id;
}
public function render()
{
if (is_null($this->selected)) {
$this->selected = option($this->id);
}
return view('vendor.option-form.select')->with([
'id' => $this->id,
'items' => $this->items,
'selected' => $this->selected
]);
}
}
class OptionFormTextarea
{
protected $id;
protected $value;
protected $rows = 3;
protected $description = "";
public function __construct($id)
{
$this->id = $id;
}
public function setContent($content)
{
$this->value = $content;
}
public function setRows($rows)
{
$this->rows = $rows;
}
public function setDescription($description)
public function description($description)
{
$this->description = $description;
return $this;
}
public function render()
{
if (is_null($this->value)) {
$this->value = option($this->id);
}
//
}
return view('vendor.option-form.textarea')->with([
'rows' => $this->rows,
'id' => $this->id,
'value' => $this->value,
'description' => $this->description
}
class OptionFormText extends OptionFormItem
{
public function render()
{
return view('vendor.option-form.text')->with([
'id' => $this->id,
'value' => $this->value
]);
}
}
class OptionFormGroup
class OptionFormCheckbox extends OptionFormItem
{
protected $id;
protected $label;
protected $items = [];
public function __construct($id)
public function label($label)
{
$this->id = $id;
$this->label = $label;
return $this;
}
public function render()
{
return view('vendor.option-form.checkbox')->with([
'id' => $this->id,
'value' => $this->value,
'label' => $this->label
]);
}
}
class OptionFormTextarea extends OptionFormItem
{
protected $rows = 3;
public function rows($rows)
{
$this->rows = $rows;
return $this;
}
public function render()
{
return view('vendor.option-form.textarea')->with([
'id' => $this->id,
'rows' => $this->rows,
'value' => $this->value
]);
}
}
class OptionFormSelect extends OptionFormItem
{
protected $options;
public function option($value, $name)
{
$this->options[] = compact('value', 'name');
return $this;
}
public function render()
{
return view('vendor.option-form.select')->with([
'id' => $this->id,
'options' => $this->options,
'selected' => $this->value
]);
}
}
class OptionFormGroup extends OptionFormItem
{
public $items = [];
public function text($id, $value = null)
{
$this->items[] = ['type' => 'text', 'id' => $id, 'value' => $value];
return $this;
}
public function addon($value)
{
$this->items[] = ['type' => 'addon', 'id' => null, 'value' => $value];
return $this;
}
public function render()
@ -347,7 +322,10 @@ class OptionFormGroup
$item['value'] = option($item['id'], null, false);
}
$rendered[] = view('vendor.option-form.'.$item['type'])->withId($item['id'])->withValue($item['value']);
$rendered[] = view('vendor.option-form.'.$item['type'])->with([
'id' => $item['id'],
'value' => $item['value']
]);
}
return view('vendor.option-form.group')->with('items', $rendered);

View File

@ -1,3 +1,3 @@
<label for="{{ $id }}">
<input {!! $checked ? 'checked="true"' : '' !!} type="checkbox" id="{{ $id }}" name="{{ $id }}" value="1"> {{ $label }}
<input {!! $value ? 'checked="true"' : '' !!} type="checkbox" id="{{ $id }}" name="{{ $id }}" value="1"> {{ $label }}
</label>

View File

@ -1,6 +1,10 @@
<tr>
<td class="key">{{ $title }} {!! $hint or '' !!}</td>
<td class="key">{{ $item->name }} {!! $item->hint or '' !!}</td>
<td class="value">
{!! $content !!}
{!! $item->render() !!}
@if ($item->description != "")
<p class="description">{!! $item->description !!}</p>
@endif
</td>
</tr>

View File

@ -1,13 +1,10 @@
<div class="box box-primary">
<div class="box box-{{ $type }}">
<div class="box-header with-border">
<h3 class="box-title">{{ $title }} {!! $hint or '' !!}</h3>
</div><!-- /.box-header -->
<form method="post">
<input type="hidden" name="option" value="{{ $id }}">
<div class="box-body">
@if (session("$id.status") == 'success')
<div class="callout callout-success">设置已保存。</div>
@endif
@if (!empty($messages))
@foreach($messages as $msg)
@ -18,7 +15,7 @@
<table class="table">
<tbody>
@foreach($items as $item)
{!! $item->render() !!}
@include('vendor.option-form.item', compact('item'))
@endforeach
</tbody>
</table>

View File

@ -1,8 +1,7 @@
<select class="form-control" name="{{ $id }}">
@foreach ((array) $items as $item)
<?php list($id, $name) = $item; ?>
<option {!! $selected == $id ? 'selected="selected"' : '' !!} value="{{ $id }}">{{ $name }}</option>
@foreach ((array) $options as $option)
<option {!! $selected == $option['value'] ? 'selected="selected"' : '' !!} value="{{ $option['value'] }}">{{ $option['name'] }}</option>
@endforeach
</select>

View File

@ -1,5 +1 @@
<textarea class="form-control" rows="{{ $rows }}" name="{{ $id }}">{{ $value }}</textarea>
@if ($description != "")
<p class="description">{!! $description !!}</p>
@endif