Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bsmithicus's avatar

How Can I Smarten Up This Form Post

I'm creating a form builder for the backend of my site. There's a bunch of buttons for various fields represented by vue dynamic components (input, textarea, radio, checkbox, etc...) and on submit I'm looping through all of the components and storing each field as the object in an array to post via axios. The problem is in my axios.post function on submit, do I seriously have to write out every col for the model in there like I'm doing below? I'm supposed to have up to 20 fields so this feels absurd but I don't know how to assign the specific object data otherwise since it's not coming from the form itself but being created before the post.

Schema:

    form
        id
        created_at
        updated_at
        field_1_name
        field_1_type
        field_1_data <= _data is always the object i create from the component data
        field_2_name
        field_2_type
        field_2_data
        field_3_name
        field_3_type
        field_3_data

Vue JS:

function checkValue(value) {
  return (value ? value : "x");
}
Vue.component('text-input', {
    props: ['count'],
    data: function() {
        return {
            type: 'text-input',
            label: '',
            description: '',
            required: ''
        }
    },
    template: `
        <div class="card text-input">
            <span class="delete-field" @click="$emit('delete-field')"><i class="pe-7s-close"></i></span>
            <div class="content">
                <div class="header">
                    <h5>Text Field</h5>
                </div>
                <div class="content">
                    <div class="form-group">
                        <label for="label">Field Label <span class="require">*</span></label>
                        <input type="text" class="form-control" name="label" v-model="label" />
                    </div>
                    <div class="form-group">
                        <label for="description">Description</label>
                        <textarea type="text" class="form-control" name="description" v-model="description"></textarea>
                        <span class="text-danger"></span>
                    </div>
                    <div class="form-check">
                        <label class="form-check-label">
                            <input class="form-check-input" name="required" type="checkbox" value="required" v-model="required">
                            <span class="form-check-sign"></span>
                            Required
                        </label>
                    </div>
                </div>
            </div>
        </div>
    `
});
Vue.component('quiz-field', {
    data: function() {
        return {
            type: 'quiz-field',
            label: '',
            description: '',
            required: '',
            answers: [
                {value:''},
                {value:''}
            ]
        }
    },
    methods: {
        addAnswer() {
            this.answers.push({ value: '' });
        },
        removeAnswer() {
            this.answers.splice(1, 1);
        }
    },
    template: `
        <div class="card quiz-field">
            <span class="delete-field" @click="$emit('delete-field')"><i class="pe-7s-close"></i></span>
            <div class="content">
                <div class="header">
                    <h5>Quiz Field</h5>
                </div>
                <div class="content">
                    <div class="form-group">
                        <label for="label">Quiz Question <span class="require">*</span></label>
                        <input type="text" class="form-control" name="label" v-model="label" />
                        <span class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label for="description">Description</label>
                        <textarea type="text" class="form-control" name="description" v-model="description"></textarea>
                        <span class="text-danger"></span>
                    </div>
                    <div class="card">
                        <div id="root" class="content input-adder">
                            <h6>Quiz Answers:</h6>
                            <div class="form-group" v-for="answer in answers">
                                <input type="text" class="form-control vcenter" v-model="answer.value"><!--
                                --><span class="is-correct vcenter" data-toggle="tooltip" data-placement="top" title="Mark as complete"><i class="pe-7s-check"></i></span>
                            </div>
                            <button class="btn btn-primary btn-sm" @click.prevent="addAnswer">Add Answer</button>
                            <button class="btn btn-danger btn-sm" @click.prevent="removeAnswer">Remove Answer</button>
                        </div>
                    </div>
                    <div class="form-check">
                        <label class="form-check-label">
                            <input class="form-check-input" name="required" type="checkbox" value="required" v-model="required">
                            <span class="form-check-sign"></span>
                            Required
                        </label>
                    </div>
                </div>
            </div>
        </div>
    `
});
new Vue({
    el: '#app',
    data: {
        inputs: [],
        title: '',
        description: '',
        type: 'before',
    },
    methods: {
        addField(type) {
            this.fieldCount += 1;
            if ('text-input' == type) {
                this.inputs.push({
                    index: 'test',
                    type: type,
                    label: '',
                    description: '',
                    required: ''
                });
            } else if ('quiz-field' == type) {
                this.inputs.push({
                    type: type,
                    fieldLabel: '',
                    fieldDescription: '',
                    required: '',
                    answers: [
                        {value:''},
                        {value:''}
                    ]
                });
            }
        },
        onDeleteField(index) {
            this.inputs.splice(index, 1);
        },
        onSubmit() {
            // Looping through form builder fields and building their data as an object
            var fields = [];
            this.$root.$children.forEach(function(element, index) {
                var type = element.type;
                if ('text-input' == type || 'text-area' == type) {
                    fields[index] = {
                        type: type,
                        label: checkValue(element.label),
                        description: checkValue(element.description),
                        required: checkValue(element.required)
                    }
                } else
                if ('quiz-field' == type) {
                    fields[index] = {
                        type: type,
                        label: checkValue(element.label),
                        description: checkValue(element.description),
                        required: checkValue(element.required),
                        answers: checkValue(element.answers)
                    }
                }
            });
            // We can only store up to 05 fields as part of a survey
            // Here we are x'ing out for fields unused
            var fieldCount = fields.length;
            for (var i = fieldCount; i<=19; i++) {
                fields[i] = {
                    type: 'x',
                    label: 'x',
                    description: 'x',
                    required: 'x'
                }
            }
            // This is where the stupidity starts
            axios.post('/surveys', {
                title: this.title,
                description: this.description,
                type: this.type,
                field_1_name: fields[0]['label'],
                field_1_type: fields[0]['type'],
                field_1_data: JSON.stringify(fields[0]),
                field_2_name: fields[1]['label'],
                field_2_type: fields[1]['type'],
                field_2_data: JSON.stringify(fields[1]),
                field_3_name: fields[2]['label'],
                field_3_type: fields[2]['type'],
                field_3_data: JSON.stringify(fields[2]),
                field_4_name: fields[3]['label'],
                field_4_type: fields[3]['type'],
                field_4_data: JSON.stringify(fields[3]),
                field_5_name: fields[4]['label'],
                field_5_type: fields[4]['type'],
                field_5_data: JSON.stringify(fields[4]),
                field_6_name: fields[5]['label'],
                field_6_type: fields[5]['type'],
                field_6_data: JSON.stringify(fields[5]),
            });
        }
    }
});
0 likes
6 replies
ahmeddabak's avatar

why not send fields to the server and let the server loop the request data, you must do a loop any way or even you can send the data to the server, i check it and you are sending every thing in the data

axios.post('/surveys', {
    data : this.$data;
});

and then only on your server you can do the logic to handle the data

bsmithicus's avatar

Thanks! When you say ' let the server loop the request data' are you suggesting working within the store function on the survey controller? Do you have an example of what that might look like? I see that you're right and my data does represent everything needed but I'm not sure I follow on how to leverage that to make this cleaner.

ahmeddabak's avatar

so if you submitted the data : this.$data , the json that is sent should look like this


{
    "data" : {
        "inputs": [
            {
              "type": "x",
              "label": "x",
              "description": "x",
              "required": "x"
             },
          {
              "type": "x",
              "label": "x",
              "description": "x",
              "required": "x"
             },
          {
              "type": "x",
              "label": "x",
              "description": "x",
              "required": "x"
             }
        ],
        "title": "The title",
        "description": "The description",
        "type": "before"
    }
}

in your store method you can access the data like this

public function store( Request $request ) {
    $title = $request->get('data')['title'];
    $description = $request->get('data')['description'];
    $type = $request->get('data')['type'];

    foreach($request->get('data')['inputs'] as $input){
        $type = $input['type'];
        $label = $input['label'];
        $description = $input['description'];
        $required = $input['required'];
    }   
}

i hope this would solve your problem

bsmithicus's avatar

Thanks so much for your response. I see that you're suggesting I build the field data in the store method. This would still leave me with really unwieldy validate/create methods in the controller though since I really need up to 20 fields. Is there no way around declaring every single column like I am below? I'm not quite sure how to build the field_#_data objects in php off the top of my head but if there were a way to slim the validate/create functions down as a result of handling the fields in the controller it would be worth figuring out.

Current store method (up to 20 fields in reality):

public function store() 
{
    $this->validate(request(), [
        'title' => 'required',
        'description' => 'required',
        'type' => 'required',
        'field_1_name' => 'nullable',
        'field_1_type' => 'nullable',
        'field_1_data' => 'nullable',
        'field_2_name' => 'nullable',
        'field_2_type' => 'nullable',
        'field_2_data' => 'nullable',
        'field_3_name' => 'nullable',
        'field_3_type' => 'nullable',
        'field_3_data' => 'nullable',
        'field_4_name' => 'nullable',
        'field_4_type' => 'nullable',
        'field_4_data' => 'nullable',
        'field_5_name' => 'nullable',
        'field_5_type' => 'nullable',
        'field_5_data' => 'nullable',
    ]);
    Survey::forceCreate([
        'title' => request('title'),
        'description' => request('description'),
        'type' => request('type'),
        'field_1_name' => request('field_1_name'),
        'field_1_type' => request('field_1_type'),
        'field_1_data' => request('field_1_data'),
        'field_2_name' => request('field_2_name'),
        'field_2_type' => request('field_2_type'),
        'field_2_data' => request('field_2_data'),
        'field_3_name' => request('field_3_name'),
        'field_3_type' => request('field_3_type'),
        'field_3_data' => request('field_3_data'),
        'field_4_name' => request('field_4_name'),
        'field_4_type' => request('field_4_type'),
        'field_4_data' => request('field_4_data'),
        'field_5_name' => request('field_5_name'),
        'field_5_type' => request('field_5_type'),
        'field_5_data' => request('field_5_data'),
    ]);
    return ['message' => 'Survey Created!'];
}
bsmithicus's avatar

Still working through this, I'm not sure I completely understand the approach you are outlining but I'm going to continue to experiment with it and will get back. Thanks so much for your time.

Please or to participate in this conversation.