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

devsam247's avatar

How to insert multiple records with createMany method in laravel

I have two related tables invoices and invoice_details table, How can I insert multiple records into the Invoice_details table with createMany method. Here's my code, I'm having SQLSTATE[HY000]: General error: 1364 Field 'service' doesn't have a default value error.

                      <div class="row">

                        <div class="col-md-12 service-group">
                              <div class="row">
                                  <div class="form-group mb-3 col-md-6">
                                      <label class="form-label">Service</label>
                                      <div >
                                          <select type="text" class="form-select" placeholder="Services" value="" name="items[][service]" id="service">
                                              <option value="" disabled selected>Select your option</option>
                                              @foreach ($services as $service)
                                                  <option value="{{$service->service_name}}"  data-id="{{$service->amount}}">{{$service->service_name}}</option>
                                              @endforeach
                                              
                                              
                                          </select>
                                          
                                      </div>
                                      </div>
          
                                      <div class="form-group mb-3 col-md-6">
                                      <label class="form-label">Amount</label>
                                      <div >
                                          <input type="text" class="form-control" name="items[][amount]" id="amount" placeholder="Amount" readonly>
                                    
                                      </div>
                                  </div>
                                  <div class="form-group mb-3 col-md-12">
                                    <label class="form-label">Description</label>
                                    <textarea class="form-control" id="description" name="items[][description]" rows="6" placeholder="Description.." ></textarea>
                                </div>
                              </div>

                        </div>

                        
                      </div>

                  </div>

//Controller

	$options = $request->only('items');
    $options_data = [];
    
    foreach ($options as $key => $value) {
         $options_data[] = $value;
    }
    
    $invoiceModel->Invoice_details()->createMany($options_data);

// Invoice_detail Model /** * Get the invoice that owns the invoice_details. */

protected $fillable = [
    'invoice_id',
    'service',
    'amount',
    'description'
];


public function invoice()
{
    return $this->belongsTo(Invoice::class);
}

// Invoice Model

public function Invoice_details()
{
    return $this->hasMany(Invoice_detail::class);
}
0 likes
32 replies
kevinbui's avatar

I have not seen a form element named items[][service] before, what about simply naming it services[] with a multiple option:

<select type="text" class="form-select" placeholder="Services" value="" name="services[]" id="service" multiple>

And what are the fields in the invoice_details table?

devsam247's avatar

@kevinbui I did name it [service] earlier but it's not working either. so, I saw that form convention here on laracasts and tried it out.

carlosbackend's avatar

Here are some possible reasons for the error:

  1. In your model, you did not add service field to your $fillable.
  2. You are sending data to the database and exempting fields that are not nullable or fields that can’t be left empty

You can try to edit invoice detail migration and set to nullable service field like this : table->string('service')->nullable()

Test and check if service field is not null in database, if is null, check how you send the form request.

devsam247's avatar

@carlosbackend checking with dd() the form has an array of data so it's not empty. I also checked my fillable and the service field is added.

devsam247's avatar

@carlosbackend

array:1 [▼ 0 => array:6 [▼ 0 => array:1 [▼ "service" => "Web Design" ] 1 => array:1 [▼ "amount" => "4500.00" ] 2 => array:1 [▼ "description" => "PHP, HTML, CSS, JavaScript, React" ] 3 => array:1 [▼ "service" => "Graphic Design" ] 4 => array:1 [▼ "amount" => "2500.00" ] 5 => array:1 [▼ "description" => "Photoshop, CorelDraw, InDesign, Figma, Illustrator" ] ] ]

rodrigo.pedra's avatar

Use $request->input(...) instead of $request->only(...).

$options = $request->input('items', []);
$options_data = [];

foreach ($options as $key => $value) {
    $options_data[] = $value;
}

$invoiceModel->Invoice_details()->createMany($options_data);

$request->only() will wrap its return value in an array keyed by the field name.

It will return something with a shape like this: [ 'items' => [ [ 'service' => ... ], ... ] ]

devsam247's avatar

@rodrigo.pedra

with $options = $request->input('items', []);

I'm getting SQLSTATE[HY000]: General error: 1364 Field 'amount' doesn't have a default value

which means the service field is sorted but the amount and description field are stuck.

rodrigo.pedra's avatar

@devsam247 what is the output of:

$options = $request->input('items', []);

dd($options);

$options_data = array_values($options);
$invoiceModel->Invoice_details()->createMany($options_data);

Also I replaced that foreach with array_values() which will yield the same results. And I actually don't think you need $options_data at all.

rodrigo.pedra's avatar

Mind that if the amount is required you should provide a default value to it, either in the database column definition, on the model, or by pre-processing the data.

Or you can enforce this value to be sent from the frontend, and validate it on the backend.

Laravel won't auto-fill any value if your frontend is sending a null value.

1. Add the required attributes to your blade:

<div class="row">
    <div class="col-md-12 service-group">
        <div class="row">
            <div class="form-group mb-3 col-md-6">
                <label class="form-label" for="service">Service</label>
                <div>
                    <select class="form-select" name="items[][service]" id="service" 
                            required>
                        <option value="" disabled selected>Select your option</option>

                        @foreach ($services as $service)
                            <option value="{{$service->service_name}}" 
                                    data-id="{{$service->amount}}">
                                {{$service->service_name}}
                            </option>
                        @endforeach
                    </select>
                </div>
            </div>

            <div class="form-group mb-3 col-md-6">
                <label class="form-label" for="amount">Amount</label>
                <div>
                    <input type="text" class="form-control" name="items[][amount]"
                           id="amount" placeholder="Amount" readonly required>
                </div>
            </div>

            <div class="form-group mb-3 col-md-12">
                <label class="form-label" for="description">Description</label>
                <textarea class="form-control" id="description" name="items[][description]"
                          rows="6" placeholder="Description.." required></textarea>
            </div>
        </div>
    </div>
</div>

2. Validate at the backend, before inserting:

$validated = $request->validate([
    'items' => ['array', 'required'],
    'items.*.service' => ['required'],
    'items.*.amount' => ['required'],
    'items.*.description' => ['required'],
]);

$invoiceModel->Invoice_details()->createMany($validated['items']);
devsam247's avatar

@rodrigo.pedra

Same error as my last reply to you. but here's what the dd($options); looks like. you can be sure that I have filled in all required fields before submitting. I don't think validation is the problem.

array:6 [▼
  0 => array:1 [▼
    "service" => "Web Design"
  ]
  1 => array:1 [▼
    "amount" => "4500.00"
  ]
  2 => array:1 [▼
    "description" => "PHP, HTML, CSS, JavaScript, React"
  ]
  3 => array:1 [▼
    "service" => "Graphic Design"
  ]
  4 => array:1 [▼
    "amount" => "2500.00"
  ]
  5 => array:1 [▼
    "description" => "Photoshop, CorelDraw, InDesign, Figma, Illustrator"
  ]
]
rodrigo.pedra's avatar

@devsam247

Look at the output you just sent:

array:6 [▼
 0 => array:1 [▼
 "service" => "Web Design" ] 
 1 => array:1 [▼
 "amount" => "4500.00" ] 
 2 => array:1 [▼
 "description" => "PHP, HTML, CSS, JavaScript, React" ] 
 3 => array:1 [▼
 "service" => "Graphic Design" ] 
 4 => array:1 [▼
 "amount" => "2500.00" ] 
 5 => array:1 [▼
 "description" => "Photoshop, CorelDraw, InDesign, Figma, Illustrator" ] 
 ]

You have 6 items, each one with only one field.

  • Field with index 0 only has service
  • Field with index 1 only has amount
  • Field with index 2 only has description
  • Field with index 3 only has service
  • Field with index 4 only has amount
  • Field with index 5 only has description

You need to add an index to the field names to group each record.

You probably have your blade fields inside an foreach, use its $loop->index:

<div class="row">
    <div class="col-md-12 service-group">
        <div class="row">
            <div class="form-group mb-3 col-md-6">
                <label class="form-label" for="service-{{ $loop->index }}">
                    Service
                </label>

                <select class="form-select" required
                        name="items[{{ $loop->index }}][service]"
                        id="service-{{ $loop->index }}">
                    <option value="" disabled selected>Select your option</option>

                    @foreach ($services as $service)
                        <option value="{{$service->service_name}}"
                                data-id="{{$service->amount}}">
                            {{$service->service_name}}
                        </option>
                    @endforeach
                </select>
            </div>

            <div class="form-group mb-3 col-md-6">
                <label class="form-label" for="amount-{{ $loop->index }}">
                    Amount
                </label>

                <input type="text" class="form-control" readonly required
                       name="items[{{ $loop->index }}][amount]"
                       id="amount-{{ $loop->index }}"
                       placeholder="Amount">
            </div>

            <div class="form-group mb-3 col-md-12">
                <label class="form-label" for="description-{{ $loop->index }}">
                    Description
                </label>

                <textarea class="form-control" rows="6" required
                          name="items[{{ $loop->index }}][description]"
                          id="description-{{ $loop->index }}"
                          placeholder="Description.."></textarea>
            </div>
        </div>
    </div>
</div>

Note I added $loop->index also to the ids, as ids should be unique. To see the benefit of it, click on a label text and see if the input is selected.

reference: https://laravel.com/docs/9.x/blade#the-loop-variable

devsam247's avatar

@rodrigo.pedra I understand your logic Rodrigo but I don't think that is what I need. Between I just tried this (what I need for the database table) and it worked, all I have to do now is pass in the form as an array, how can I do that, please?

$invoiceModel->Invoice_details()->createMany([
            [
                'service' => 'First Service',  
                'amount' => 2334.00,
                'description' => 'First Description',
            ],
            [
                'service' => 'Second Service',
                'amount' => 5334.00,
                'description' => 'Second Description',
            ],
        ]);
       
rodrigo.pedra's avatar

@devsam247

how can I do that, please?

My last answer will precisely send the array like you describe. Have you tried it? Or you just think it is not what you need?

devsam247's avatar

@rodrigo.pedra how do you mean I have my blade field inside a foreach? As you can see the foreach statement I have is for the select inputs.

Can you share a working example?

rodrigo.pedra's avatar

@devsam247

On your dd() output you had posted two sets of service, amount and description, so you are repeating that blade template somehow.

Can you share how are you repeating it?

But the idea, is that to group the fields you need a common index on the first array index, so PHP groups then when processing the input.

For example, the first set of fields should have fields with their name property as:

  • items[0][service]
  • items[0][amount]
  • items[0][description]

So PHP knows they form a single nested array on the first index of the items array, then on the second set:

  • items[1][service]
  • items[1][amount]
  • items[1][description]

And so on.

The reason that before it was breaking into items with a single field is that if we look how the fields were named before:

  • items[][service]
  • items[][amount]
  • items[][description]

There is no way PHP can guess we want the second indices to be grouped into the same nested array. So it ends up splitting then on individual items.

1 like
devsam247's avatar

@rodrigo.pedra That's because the service, amount, and description fields are dynamic. Users can add more of these fields when inserting their data.

The implementation was cloning the index fields (service, amount, and description ) on addmore click using javascript so the new field will share the same attributes as the index fields.

rodrigo.pedra's avatar

@devsam247 so in this JavaScript code you are cloning the template you need to replace the first index of each field with a common index, so the fields can be grouped.

If you care to share the full template, and the JavaScript code which duplicates it, I can take a look on how you can do it.

devsam247's avatar

@rodrigo.pedra Do let me know if you need any clarification.

@extends('layouts.app')

@section('content')
        <div class="container-xl">
          <!-- Page title -->
          <div class="page-header d-print-none">
            <div class="row g-2 align-items-center">
              <div class="col">
                <!-- Page pre-title -->
                <div class="page-pretitle">
                  Overview
                </div>
                <h2 class="page-title">
                    Invoice
                </h2>
              </div>
             
            </div>
          </div>
        </div>
        <div class="page-body">
          <div class="container-xl">
            <div class="row row-deck row-cards">

              <div class="col-md-12">
                <div class="card">
                  <div class="card-header">
                    <h3 class="card-title">Invoice Information</h3>
                  </div>

                  <div class="error">
                    @if ($errors->any())
                        <div class="alert alert-danger">
                            <ul>
                                @foreach ($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        </div>
                    @endif
                  </div>


                  <div class="card-body">
                    <form action="{{route('store-invoice')}}" method="POST">
                      @csrf

                      <!--Fullname & Phone No -->
                      <div class="row">

                        <div class="form-group mb-3 col-md-6">
                          <label class="form-label">Client FullName</label>
                          <div >
                         

                            <select type="text" class="form-select" placeholder="Client Fullname" id="select-client" name="client-id">
                                @foreach ($clients as $client)
                                    <option value="{{$client->id}}" data-id="{{$client->phone}}">{{$client->full_name}}</option>
                                @endforeach
                            </select>

                            
      
                          </div>
                        </div>
  
                        <div class="form-group mb-3 col-md-6">
                          <label class="form-label">Client Phone</label>
                          <div >

                            
                            <input type="text" class="form-control" id="client-phone" name="client-phone" placeholder="Phone" disabled>
                           
                          </div>
                        </div>
  
                      </div>
                      <!--/Fullname & Phone No -->



                      <hr/>
                      <!--Service & Description -->
                      <div class="service-box">

                          <div class="row">

                            <div class="col-md-12 service-group">
                                  <div class="row">
                                      <div class="form-group mb-3 col-md-6">
                                          <label class="form-label">Service</label>
                                          <div >
                                              <select type="text" class="form-select" placeholder="Services" value="" name="service[]" id="service" required>
                                                  <option value="" disabled selected>Select your option</option>
                                                  @foreach ($services as $service)
                                                      <option value="{{$service->service_name}}"  data-id="{{$service->amount}}">{{$service->service_name}}</option>
                                                  @endforeach
                                                  
                                                  
                                              </select>
                                              
                                          </div>
                                          </div>
              
                                          <div class="form-group mb-3 col-md-6">
                                          <label class="form-label">Amount</label>
                                          <div >
                                              <input type="text" class="form-control" name="amount[]" id="amount" placeholder="Amount" readonly required>
                                        
                                          </div>
                                      </div>
                                      <div class="form-group mb-3 col-md-12">
                                        <label class="form-label">Description</label>
                                        <textarea class="form-control" id="description" name="description[]" rows="6" placeholder="Description.." required></textarea>
                                    </div>
                                  </div>

                            </div>

                            
                          </div>

                      </div>

                      <div class="more-service-box"></div>

                      <div class="col-md-12">
                          <button type="button" id="addmore" class="btn btn-default"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                            <circle cx="12" cy="12" r="9"></circle>
                            <line x1="9" y1="12" x2="15" y2="12"></line>
                            <line x1="12" y1="9" x2="12" y2="15"></line>
                        </svg> Add More</button>
                          <button type="button"   id="remove" class="btn btn-default"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-minus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                            <circle cx="12" cy="12" r="9"></circle>
                            <line x1="9" y1="12" x2="15" y2="12"></line>
                        </svg> Remove</button>
                      </div>
                     
                      <!--/ Service & Description -->
                      <hr/>
                      


                      
                      <!--Payment -->
                      <div class="row">

                        <div class="form-group mb-3 col-md">
                            <label class="form-label">VAT Due</label>
                            <div >
                              <input type="text" class="form-control" name="vat-due" placeholder="VAT Due">
                              
                            </div>
                        </div>
                        

                        <div class="form-group mb-3 col-md">
                            <label class="form-label">Sub Total</label>
                            <div >
                              <input type="text" class="form-control" name="sub-total" placeholder="Sub Total">
                              
                            </div>
                        </div>

                        
  
                        <div class="form-group mb-3 col-md">
                          <label class="form-label">Total Amount</label>
                          <div >
                            <input type="text" class="form-control" name="total-amount" placeholder="Total Amount">
                           
                          </div>
                        </div>


                        <div class="form-group mb-3 col-md">
                            <label class="form-label">Issue Date</label>
                            <div class="input-icon mb-2">
                                <input type="date" id="date" class="date" name="issue-date"
                                min="2022-01-01">
                            </div>
                        </div>
                       

                        <div class="form-group mb-3 col-md">
                            <label class="form-label">Due Date</label>
                            <div class="input-icon mb-2">
                                <input type="date" id="date" class="date" name="due-date"
                                min="2022-01-01">
                            </div>
                        </div>



                      </div>
                      <!--/ Payment -->
                      
                        
                      
                    
                      <div class="form-footer">
                        <button type="submit" class="btn btn-primary"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-send" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                          <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                          <line x1="10" y1="14" x2="21" y2="3"></line>
                          <path d="M21 3l-6.5 18a0.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a0.55 .55 0 0 1 0 -1l18 -6.5"></path>
                       </svg> Submit</button>
                      </div>
                    </form>
                  </div>
                </div>
              </div>
             
            </div>
          </div>
        </div>
       
     
    
@endsection




// Add Service , Amount, Description

$("#addmore").click(function() {
    $('.service-group:first').clone().find("input:text,textarea").val("").end()
      .appendTo('.more-service-box');
});

$("#remove").on("click", function() {  
    $(".more-service-box").children().last().remove();  
});  

rodrigo.pedra's avatar

@devsam247 maybe this will do the trick in your code:

// Add Service , Amount, Description
$('#addmore').click(function () {
    var $service = $('.service-group:first').clone();

    var index = $('.service-group').length;

    $service.find(['select[name*=service]'])
        .val('')
        .attr('id', 'item[' + index + '][service]');

    $service.find(['[name*=amount]'])
        .val('')
        .attr('id', 'item[' + index + '][amount]');

    $service.find(['textarea[name*=description]'])
        .val('')
        .attr('id', 'item[' + index + '][description]');

    $service.appendTo('.more-service-box');
});
1 like
rodrigo.pedra's avatar

@devsam247

I updated my repo for updating existing, allow removing, and clone the template for improved memory usage.

devsam247's avatar

@rodrigo.pedra Thanks, Rodrigo. I will have a look and get back to you. do you mind sharing your skype contact?

devsam247's avatar

@rodrigo.pedra The javascript does not do the trick. I also noticed even after creating a form that outputs like this, it still does not submit to the database. Getting General error: 1364 Field 'service' doesn't have a default value error.

array:1 [▼
  "items" => array:2 [▼
    0 => array:3 [▼
      "service" => "Web Design"
      "amount" => "4500.00"
      "description" => "PHP, HTML, CSS, JavaScript, React"
    ]
    1 => array:3 [▼
      "service" => "Graphic Design"
      "amount" => "2500.00"
      "description" => "Photoshop, CorelDraw, InDesign, Figma, Illustrator"
    ]
  ]
]
rodrigo.pedra's avatar

@devsam247

Two things:

First, you are right, my javascript have a mistake, the name attribute should change like that, try this:

// Add Service , Amount, Description
$('#addmore').click(function () {
    var $service = $('.service-group:first').clone();

    var index = $('.service-group').length;

    $service.find(['select[name*=service]'])
        .val('')
        .attr('name', 'item[' + index + '][service]')
        .attr('id', 'service-' + index);

    $service.find(['input[name*=amount]'])
        .val('')
        .attr('name', 'item[' + index + '][amount]')
        .attr('id', 'amount-' + index);

    $service.find(['textarea[name*=description]'])
        .val('')
        .attr('name', 'item[' + index + '][description]')
        .attr('id', 'description-' + index);

    $service.appendTo('.more-service-box');
});
rodrigo.pedra's avatar

@devsam247 also, share your controller code, your dd output has the items key wrapping your records as we had many messages before

devsam247's avatar

@rodrigo.pedra The Javascript still won't add the index value on the name attributes maybe because the elements are dynamic. How do we change the item from key wrapping the records to an item of an array createMany() method can take as I have (manually) my record grouped in each item like this?

items[0][service]

items[0][amount]

items[0][description]

items[1][service]

items[1][amount]

items[1][description]

array:1 [▼
  "items" => array:2 [▼
    0 => array:3 [▼
      "service" => "Web Design"
      "amount" => "4500.00"
      "description" => "PHP, HTML, CSS, JavaScript, React"
    ]
    1 => array:3 [▼
      "service" => "Graphic Design"
      "amount" => "2500.00"
      "description" => "Photoshop, CorelDraw, InDesign, Figma, Illustrator"
    ]
  ]
]

rodrigo.pedra's avatar

@devsam247

Did you see this message?

also, share your controller code, your dd output has the items key wrapping your records as we had many messages before

Also have you seen the repository code?

rodrigo.pedra's avatar

There were some typos:

$('#addmore').click(function () {
    var index = $('.service-group').length;
    var $service = $('.service-group:first').clone();

    $service.find('select[name*=service]')
        .val('')
        .attr('name', 'items[' + index + '][service]')
        .attr('id', 'service-' + index);

    $service.find('input[name*=amount]')
        .val('')
        .attr('name', 'items[' + index + '][amount]')
        .attr('id', 'amount-' + index);

    $service.find('textarea[name*=description]')
        .val('')
        .attr('name', 'items[' + index + '][description]')
        .attr('id', 'description-' + index);

    $service.appendTo('.more-service-box');
});

I also added it to the repo. Serve the app and check the /alternative route.

1 like
devsam247's avatar

@rodrigo.pedra The Javascript works now. Here's my controller. I'm getting SQLSTATE[HY000]: General error: 1364 Field 'service' doesn't have a default value error.

$options = $request->only('items');
 dd($options);

 $options_data = [];
        
 foreach ($options as $key => $value) {
      $options_data[] = $value;
  }
        
 $invoiceModel->Invoice_details()->createMany($options_data);

Here's the dd output

array:1 [▼
  "items" => array:4 [▼
    0 => array:3 [▼
      "service" => "Web Design"
      "amount" => "4500.00"
      "description" => "PHP, HTML, CSS, JavaScript, React, Python."
    ]
    1 => array:3 [▼
      "service" => "Graphic Design"
      "amount" => "2500.00"
      "description" => "Illustrator, InDesign, CorelDraw, Photoshop, Figma"
    ]
    2 => array:3 [▼
      "service" => "Web Design"
      "amount" => "4500.00"
      "description" => "PHP, HTML, CSS, JavaScript, React, Python."
    ]
    3 => array:3 [▼
      "service" => "Graphic Design"
      "amount" => "2500.00"
      "description" => "Illustrator, InDesign, CorelDraw, Photoshop, Figma"
    ]
  ]
]
devsam247's avatar

@rodrigo.pedra Everything is working seamlessly, now that I changed my controller. Thanks for your assistance, I so much appreciate your time. Would you like in any way to share your personal contact with me for future projects?

$options = $request->input('items', []);
$invoiceModel->Invoice_details()->createMany($options);
1 like
rodrigo.pedra's avatar

@devsam247 glad to hear it is working.

Unfortunately I am not available for new projects in the foreseeable future. But thanks anyway =)

Please or to participate in this conversation.