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

SupaMonkey's avatar

How to import a single excel file/sheet with various Models (and sub-data/models) in Laravel?

As per the title, I have a single excel file full of data (parent/child) in each row. The Laravel Excel site shows how to import, assuming a single model per row - but how does one import the child data? https://laravel-excel.maatwebsite.nl/3.1/imports/

eg 'pseudo' schema:

    Schema::create('students', function (Blueprint $table) {
        $table->increments('id')->unsigned()->index();
        $table->string('student_code',16);
        $table->string('student_name',64);
        $table->string('student_surname',64);
    });
    Schema::create('student_courses', function (Blueprint $table) {
        $table->increments('id')->unsigned()->index();
        $table->integer('student_id')->unsigned()->index();
        $table->string('course', 32);
        $table->date('start_date');
        $table->date('end_date');
        $table->timestamps();
    });
    Schema::create('student_contacts', function (Blueprint $table) {
        $table->increments('id')->unsigned()->index();
        $table->integer('student_id')->unsigned()->index();
        $table->string('contact_name', 32);
        $table->string('contact_number', 32);
        $table->timestamps();
    });

Eg File: students.xlsx

Student Code | Student Name | Student Surname | Course  | Course Start Date | Course End Date | Contact Person | Contact Numbers
ABC1         | Arnold       | Clarp           | C++     | 2019-01-01        | 2019-12-01      | Boogle         | 555-111-222
DEF2         | Delta        | Flork           | English | 2019-01-02        | 2019-12-02      | Google         | 555-111-333
GHI3         | Goblin       | Clark           | Science | 2019-01-03        | 2019-12-03      | Foogle         | 555-111-444

Assuming my import code:

class StudentsImport implements ToModel, WithStartRow
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
    return new Student([
        'student_code'                  => $row[1],
        'student_name'              => $row[2],
        'student_surname'               => $row[3],
    ]);
}

/**
* @return int
*/
public function startRow(): int
{
    return 2;
}
}

Where would I actually go about plugging in the import of the "child" course/contact data? I'm guessing that instead of 'returning a new student' - I would actually first assign it to a variable, then import it then? Is this the correct way? eg:

public function model(array $row)
{
    $student = new Student([
        'student_code'=> $row[1],
        'student_name'=> $row[2],
        'student_surname'=> $row[3],
    ])
    $student->courses()->create([
        'course'=>$row[4],
        'start_date'=>$row[5],
        'end_date'=>$row[6]
    ]);
    $student->contacts()->create([
        'contact_name'=>$row[7],
        'contact_number'=>$row[8]
    ]);
    return $student;
}

//actual code as it stands, no longer 'pseudo':

    $student = new Student([
        'bursary_provider_id' => 1,
        'bursary_provider_reference' => 'xxx',
        'student_name' => $row[1],
        'student_initials' => $row[3],
        'student_surname' => $row[2],
        'passport_number' => $row[7],
        'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
        'country_id' => 7,
        'id_number' => $row[6],
        'status' => $status,
        'notes' => $row[5]
    ]);

    if (isset($row[10])) {
        $student->visas()->create([
            'country_id' => 201,
            'visa_reference_number' => $row[10],
            'visa_expiration_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[11])
        ]);

Error (its not passing across the parent id - from my understanding is that it hasnt yet created the parent at this stage of the code so there is no id to pass across)

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'student_id' cannot be null (SQL: insert into student_visas (country_id, visa_reference_number, visa_expiration_date, student_id, updated_at, created_at) values (201, ABCHFV4, 2019-12-31 00:00:00, , 2019-01-11 08:03:06, 2019-01-11 08:03:06))
0 likes
5 replies
aurawindsurfing's avatar

You are on a good track but you are not providing student_id while doing the import. This is exactly what error says:

Column 'student_id' cannot be null

there is no student_id here:

 $student = new Student([
        'bursary_provider_id' => 1,
        'bursary_provider_reference' => 'xxx',
        'student_name' => $row[1],
        'student_initials' => $row[3],
        'student_surname' => $row[2],
        'passport_number' => $row[7],
        'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
        'country_id' => 7,
        'id_number' => $row[6],
        'status' => $status,
        'notes' => $row[5]
    ]);

With excel imports you have to go step by step until you eliminate all errors. Once you do that you will be flying with 5000 rows at a time ;-)

Hope it helps!

SupaMonkey's avatar

Hi @aurawindsurfing Thanks for the response, but I dont think you are understanding the situation clearly. I have an excel sheet full of students and then their relational data on the same row (eg: contacts / visas / etc). At the section you've mentioned, when I am stipulating the 'new Student()'; there is no student_id because:

  1. At student level, its the "id" field
  2. There is no "id", its auto_increment and set at insertion

When I try $student->visas()->create() - its failing because there is no student_id because the "new Student()" hasnt actually been inserted into the database. Laravel Excel doesnt appear to insert on the fly; seems to do it as a batch at the end of reading/parsing the entire sheet/file.

aurawindsurfing's avatar
Level 50

Then do this:

use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;

class BookingImport implements ToCollection, WithHeadingRow
{

    public function chunkSize(): int
    {
        return 500;
    }

    public function collection(Collection $rows)
    {
        foreach ($rows as $row) {

this way you can also use real row names while doing import.

and then:

$student = Student::updateOrCreate([
        'bursary_provider_id' => 1,
        'bursary_provider_reference' => 'xxx',
        'student_name' => $row[1],
        'student_initials' => $row[3],
        'student_surname' => $row[2],
        'passport_number' => $row[7],
        'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
        'country_id' => 7,
        'id_number' => $row[6],
        'status' => $status,
        'notes' => $row[5]
    ]);

Hope it helps!

1 like
SupaMonkey's avatar

That did the trick! Thank you!

PS: For anyone looking into this in the future, the only thing left out from @aurawindsurfing 's answer was the inclusion of "use Illuminate\Support\Collection;" I also added the first parameter to the 'updateOrCreate()' to allow for a 'where x=y' for the update part of the 'updateOrCreate()'

1 like
codegeekteam's avatar

I found your exchange VERY helpful @aurawindsurfing and @supamonkey . I'm a bit early in my learning, so I appreciate the answers and explanations. I am hoping this will help someone out. I wanted to show my "child" models and how I implemented the "parent" (as in SupaMonkey's "student"). And I'm also tying each "client" to the logged in user, which is also super helpful.

use App\Models\Client;
use Illuminate\Support\Str;
use App\Models\ClientContact;
use App\Models\ClientDomain;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;

class ClientImport implements WithHeadingRow, ToCollection
{
    public function collection(Collection $rows)
    {
        $userid = auth()->user()->id;
        foreach ($rows as $row) {
            $slug = Str::slug($row['companyname'], '-');
            $clientID = Client::create([
                'user_id' => $userid,
                'companyName' => $row['companyname'],
                'companySlug' => $slug,
                'companyAddress1' => $row['companyaddress1'],
                'companyCity' => $row['companycity'],
                'companyState' => $row['companystate'],
            ]);
            ClientContact::create([
                'client_id' => $clientID->id,
                'contactFirst' => $row['contactfirst'],
                'contactLast' => $row['contactlast'],
                'contactEmail' => $row['contactemail'],
            ]);

            ClientDomain::create([
                'client_id' => $clientID->id,
                'companyWebsite' => $row['companywebsite'],
                'webHost' => $row['webhost'],
            ]);
        }
    }
}

Please or to participate in this conversation.