Skip to content

Commit

Permalink
Added "add database columns" to form builder (#312)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-kuendig authored Sep 18, 2021
1 parent 0250235 commit 5807465
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 2 deletions.
46 changes: 46 additions & 0 deletions assets/js/builder.index.entity.modelform.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,52 @@
)
}

ModelForm.prototype.cmdAddFieldsFromDatabase = function (ev) {
var $target = $(ev.currentTarget)

// Always use the first placeholder to add controls
var $placeholder = this.getMasterTabsActivePane().find('.builder-control-list .control.placeholder:first')[0]

// Filter all fields from the DataTable that have the "add" checkbox checked.
var fields = $target.find('.control-table').data('oc.table').dataSource.data.filter(function (column) {
return column.add
}).reverse()

// Hide the poup and initialize the load indicator.
$target.closest('.control-popup').data('oc.popup').hide()
$.oc.stripeLoadIndicator.show()

// When a control is added, an AJAX request is made which returns the widget's markup.
// We need to wait for each request to finish before we can add another field, since the
// addControlToPlaceholder requires a proper reflow of the whole form layout before
// a new field can be added. This addField helper function makes sure that all
// Promises are run in sequence to achieve this.
function addField (field) {
return function () {
var defer = $.Deferred()
$.oc.builder.formbuilder.controller.addControlToPlaceholder(
$placeholder,
field.type,
field.label ? field.label : field.column,
false,
field.column
).complete(function () {
defer.resolve()
})
return defer.promise()
};
}

/// Add all fields in sequence.
var allFields = $.when({})
$.each(fields, function (index, field) {
allFields = allFields.then(addField(field))
});

// Once everything is done, hide the load indicator.
$.when(allFields).always($.oc.builder.indexController.hideStripeIndicatorProxy)
}

ModelForm.prototype.cmdOpenForm = function(ev) {
var form = $(ev.currentTarget).data('form'),
model = $(ev.currentTarget).data('modelClass')
Expand Down
94 changes: 94 additions & 0 deletions behaviors/IndexModelFormOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\FormWidgets\FormBuilder;
use RainLab\Builder\Classes\ModelModel;
use RainLab\Builder\Classes\ControlLibrary;
use Backend\Classes\FormField;
use Backend\FormWidgets\DataTable;
use ApplicationException;
use Exception;
use Request;
Expand Down Expand Up @@ -127,6 +129,24 @@ public function onModelFormGetModelFields()
];
}

public function onModelShowAddFieldsFromDatabasePopup()
{
$columns = ModelModel::getModelColumnsAndTypes($this->getPluginCode(), Input::get('model_class'));
$config = $this->makeConfig($this->getAddFieldsFromDatabaseDataTableConfig());

$field = new FormField('add_fields_from_database', 'add_fields_from_database');
$field->value = $this->getAddFieldsFromDatabaseDataTableValue($columns);

$datatable = new DataTable($this->controller, $field, $config);
$datatable->alias = 'add_fields_from_database_datatable';
$datatable->bindToController();

return $this->makePartial('add-fields-from-database-popup-form', [
'datatable' => $datatable,
'pluginCode' => $this->getPluginCode()->toCode(),
]);
}

protected function loadOrCreateFormFromPost()
{
$pluginCode = Request::input('plugin_code');
Expand Down Expand Up @@ -183,4 +203,78 @@ protected function mergeRegistryDataIntoResult(&$result, $model, $modelClass)
'modelClass' => $fullClassName
];
}

/**
* Returns the configuration for the DataTable widget that
* is used in the "add fields from database" popup.
*
* @return array
*/
protected function getAddFieldsFromDatabaseDataTableConfig()
{
// Get all registered controls and build an array that uses the control types as key and value for each entry.
$controls = ControlLibrary::instance()->listControls();
$fieldTypes = array_merge(array_keys($controls['Standard']), array_keys($controls['Widgets']));
$options = array_combine($fieldTypes, $fieldTypes);

return [
'toolbar' => false,
'columns' => [
'add' => [
'title' => 'rainlab.builder::lang.common.add',
'type' => 'checkbox',
'width' => '50px',
],
'column' => [
'title' => 'rainlab.builder::lang.database.column_name_name',
'readOnly' => true,
],
'label' => [
'title' => 'rainlab.builder::lang.list.column_name_label',
],
'type' => [
'title' => 'rainlab.builder::lang.form.control_widget_type',
'type' => 'dropdown',
'options' => $options,
],
],
];
}

/**
* Returns the initial value for the DataTable widget that
* is used in the "add database columns" popup.
*
* @param array $columns
*
* @return array
*/
protected function getAddFieldsFromDatabaseDataTableValue(array $columns)
{
// Map database column types to widget types.
$typeMap = [
'string' => 'text',
'integer' => 'number',
'text' => 'textarea',
'timestamp' => 'datepicker',
'smallInteger' => 'number',
'bigInteger' => 'number',
'date' => 'datepicker',
'time' => 'datepicker',
'dateTime' => 'datepicker',
'binary' => 'checkbox',
'boolean' => 'checkbox',
'decimal' => 'number',
'double' => 'number',
];

return array_map(function ($column) use ($typeMap) {
return [
'column' => $column['name'],
'label' => str_replace('_', ' ', ucfirst($column['name'])),
'type' => $typeMap[$column['type']] ?? $column['type'],
'add' => false,
];
}, $columns);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?= Form::open([
'data-builder-command'=>'modelForm:cmdAddFieldsFromDatabase'
]) ?>

<div class="modal-header">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.form.btn_add_fields_from_database')) ?></h4>
</div>

<div class="modal-body">
<?= $datatable->render(); ?>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary">
<?= e(trans('backend::lang.form.apply')) ?>
</button>
<button
type="button"
class="btn btn-default"
data-dismiss="popup">
<?= e(trans('backend::lang.form.cancel')) ?>
</button>

<input type="hidden" name="plugin_code" value="<?= e($pluginCode) ?>">
</div>
<?= Form::close() ?>
10 changes: 10 additions & 0 deletions behaviors/indexmodelformoperations/partials/_toolbar.htm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
<?= e(trans('backend::lang.form.save')) ?>
</a>

<a
href="javascript:;"
class="btn btn-primary oc-icon-magic"
data-control="popup"
data-handler="onModelShowAddFieldsFromDatabasePopup"
data-stripe-load-indicator
>
<?= e(trans('rainlab.builder::lang.form.btn_add_fields_from_database')) ?>
</a>

<button
type="button"
class="btn btn-default empty oc-icon-trash-o <?php if (!strlen($fileName)): ?>hide<?php endif ?>"
Expand Down
6 changes: 4 additions & 2 deletions formwidgets/formbuilder/assets/js/formbuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
return fieldName
}

FormBuilder.prototype.addControlToPlaceholder = function(placeholder, controlType, controlName, noNewPlaceholder) {
FormBuilder.prototype.addControlToPlaceholder = function(placeholder, controlType, controlName, noNewPlaceholder, fieldName) {
// Duplicate the placeholder and place it after
// the existing one
if (!noNewPlaceholder) {
Expand All @@ -420,7 +420,9 @@
placeholder.innerHTML = ''
placeholder.removeAttribute('data-builder-placeholder')

var fieldName = this.generateFieldName(controlType, placeholder)
if (!fieldName) {
fieldName = this.generateFieldName(controlType, placeholder)
}

// Send request to the server to load the
// control markup, Inspector data schema, inspector title, etc.
Expand Down
2 changes: 2 additions & 0 deletions lang/en/lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
'saved' => 'Form saved',
'confirm_delete' => 'Delete the form?',
'tab_new_form' => 'New form',
'btn_add_fields_from_database' => 'Add fields from database',
'property_label_title' => 'Label',
'property_label_required' => 'Please specify the control label.',
'property_span_title' => 'Span',
Expand Down Expand Up @@ -363,6 +364,7 @@
'control_mediafinder_description' => 'Field for selecting an item from the Media Manager library',
'control_relation' => 'Relation',
'control_relation_description' => 'Displays either a dropdown or checkbox list for selecting a related record',
'control_widget_type' => 'Widget Type',
'error_file_name_required' => 'Please enter the form file name.',
'error_file_name_invalid' => 'The file name can contain only Latin letters, digits, underscores, dots and hashes.',
'span_left' => 'Left',
Expand Down

0 comments on commit 5807465

Please sign in to comment.