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

Jdagger's avatar

Attempt to read property "email" on null | Laravel 11.x

Hey guys, I have a weird problem which I can't replicate the problem with a working version because I didn't use version control (lesson learned). Anyway, I have trouble with the pagination method and data fetching while going to the next page of data in a table, shown in the app. Thing is, the first page works perfectly fine. I'm getting the ErrorException [title name] error when I skip to another page of the table. Very weird stuff since it worked before..

The idea of it is that a user (gebruiker in Dutch) hasmany() registrations (registraties in Dutch) and for( every registration) I fetch the data and show it in a table for all to see.

Why does it only show the first page of my table and not fetch the data correctly (which I'm assuming is the case) on the second page of the paginated table?

Fetching data method:

    public function showTableAll(User $user, Registratie $registraties) {
        $registraties = Registratie::with('gebruiker')->paginate(5);
        
        return view('table-all', [
            'registraties' => $registraties
        ]);
    }

Table html:

        <table class="table table-hover" id="tableAll">
            <thead>
                <tr>
                    <th onclick="sortTable(0)"><strong>Lidnummer</strong></th>
                    <th onclick="sortTable(1)"><strong>Geslacht</strong></th>
                    <th onclick="sortTable(2)"><strong>Soort</strong></th>
                    <th onclick="sortTable(3)"><strong>Vangplaats</strong></th>
                    <th onclick="sortTable(4)"><strong>AS</strong></th>
                    <th onclick="sortTable(5)"><strong>KV</strong></th>
                    <th onclick="sortTable(6)"><strong>Notitie</strong></th>
                    <th onclick="sortTable(7)"><strong>Ondersoort</strong></th>
                    <th onclick="sortTable(8)"><strong>M/V</strong></th>
                    <th onclick="sortTable(9)"><strong>Aantal</strong></th>
                    <th onclick="sortTable(10)"><strong>Groep</strong></th>
                    <th onclick="sortTable(11)"><strong>Jongen</strong></th>
                </tr>
            </thead>
            <tr>
            @foreach ($registraties as $registratie)
                <tr>
                    <td> <a href="mailto:{{ $registratie->gebruiker->email }}">{{ $registratie->gebruiker->lidnummer }}</a></td>
                    <td>{{ $registratie->geslachtsnaam }}</td>
                    <td>{{ $registratie->soortnaam }}</td>
                    <td>{{ $registratie->vangplaats }}</td>
                    <td>{{ $registratie->AS }}</td>
                    <td>{{ $registratie->KV }}</td>
                    <td>{{ $registratie->notitie }}</td>
                    <td>{{ $registratie->ondersoort }}</td>
                    <td>{{ $registratie->mv }}</td>
                    <td>{{ $registratie->aantal }}</td>
                    <td>{{ $registratie->groep }}</td>
                    <td>{{ $registratie->jongen }}</td>
                </tr>
            @endforeach
            {{ $registraties->links() }}
            </tr>
        </table>

Registratie model:

<?php

namespace App\Models;

use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Registratie extends Model
{
    use HasFactory;

    public $table = 'registraties';

    protected $fillable = [
        "user_id",
        "geslachtsnaam",
        "soortnaam",
        "vangplaats",
        "AS",
        "KV",
        "notitie",
        "ondersoort",
        "aantal",
        "mv",
        "groep",
        "jongen"
    ];

    public function gebruiker() {
        return $this->belongsTo(User::class, "user_id");
    }
}

User model:

<?php

namespace App\Models;

use App\Models\Registratie;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'lidnummer',
        'naam',
        'achternaam',
        'email',
        'password',
        'verified',
        'password_updated_at'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];

    public function registraties() {
        return $this->hasMany(Registratie::class, 'user_id', 'id');
    }
}

Some Javascript for the table, don't know if it's relevant but:

     <script>
      
      console.log('hello :)');

      // Export table data to Excel file
      var btnXlsx = document.querySelectorAll('.action button')[0];
      var btnXls = document.querySelectorAll('.action button')[1];
      var btnCvs = document.querySelectorAll('.action button')[2];

      btnXlsx.onclick = () => exportData('xlsx');
      btnXls.onclick = () => exportData('xls');
      btnCvs.onclick = () => exportData('csv');

      function exportData(type) {
        
          const fileName = 'exported-sheet.' + type;
          const table = document.getElementById("tableAll");
          const wb = XLSX.utils.table_to_book(table);
          XLSX.writeFile(wb, fileName); 
      }

        // Search through table data
      function myFunction() {
        // Declare variables
        var input, filter, table, tr, td, i, txtValue;
        input = document.getElementById("myInput");
        filter = input.value.toUpperCase();
        table = document.getElementById("myTable");
        tr = table.getElementsByTagName("tr");

        // Loop through all table rows, and hide those who don't match the search query
        for (i = 0; i < tr.length; i++) {
          td = tr[i].getElementsByTagName("td")[0];
          if (td) {
            txtValue = td.textContent || td.innerText;
            if (txtValue.toUpperCase().indexOf(filter) > -1) {
              tr[i].style.display = "";
            } else {
              tr[i].style.display = "none";
            }
          }
        }
      }

        // Search through table data
        function searchFunction() {
          // Declare variables
          var input, filter, table, tr, td, i, txtValue;
          input = document.getElementById("searchInput");
          filter = input.value.toUpperCase();
          table = document.getElementById("tableAll");
          tr = table.getElementsByTagName("tr");

          // Loop through all table rows, and hide those who don't match the search query
          for (i = 0; i < tr.length; i++) {
            td = tr[i].getElementsByTagName("td")[0];
            if (td) {
              txtValue = td.textContent || td.innerText;
              if (txtValue.toUpperCase().indexOf(filter) > -1) {
                tr[i].style.display = "";
              } else {
                tr[i].style.display = "none";
              }
            }
          }
        }

        function sortTable(n) {
          var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
          table = document.getElementById("tableAll");
          switching = true;
          //Set the sorting direction to ascending:
          dir = "asc"; 
          /*Make a loop that will continue until
          no switching has been done:*/
          while (switching) {
            //start by saying: no switching is done:
            switching = false;
            rows = table.rows;
            /*Loop through all table rows (except the
            first, which contains table headers):*/
            for (i = 1; i < (rows.length - 1); i++) {
                //start by saying there should be no switching:
                shouldSwitch = false;
                /*Get the two elements you want to compare,
                one from current row and one from the next:*/
                x = rows[i].getElementsByTagName("TD")[n];
                y = rows[i + 1].getElementsByTagName("TD")[n];
                /*check if the two rows should switch place,
                based on the direction, asc or desc:*/
                if (dir == "asc") {
                    if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
                        //if so, mark as a switch and break the loop:
                        shouldSwitch = true;
                        break;
                    }
                } else if (dir == "desc") {
                    if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
                        //if so, mark as a switch and break the loop:
                        shouldSwitch = true;
                        break;
                    }
                }
            }

            if (shouldSwitch) {
                /*If a switch has been marked, make the switch
                and mark that a switch has been done:*/
                rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
                switching = true;
                //Each time a switch is done, increase this count by 1:
                switchcount++;
            } else {
                /*If no switching has been done AND the direction is "asc",
                set the direction to "desc" and run the while loop again.*/
                if (switchcount == 0 && dir == "asc") {
                    dir = "desc";
                    switching = true;
                }
              }
            }
          }
    </script> 

Pls help 🥹

0 likes
2 replies
tykus's avatar
tykus
Best Answer
Level 104

Most probably the error message originates from this $registratie->gebruiker->email statement; if the current ((in the loop) Registratie instance has no associated gebruiker. This might be because there is an invalid value in the foreign key; or there is a query scope that is excluding a seemingly valid FK value.

Try this instead:

<td>
  @if ($registratie->gebruiker)
    <a href="mailto:{{ $registratie->gebruiker->email }}">{{ $registratie->gebruiker->lidnummer }}</a>
  @endif
</td>
1 like
Jdagger's avatar

@tykus Yes it did come from there, sorry for not clarifying that. I might have made a user/registration relation that had some empty data in there, but I can't find any entries that have null values.. very weird.

Your solution for checking if $registratie->gebruiker == true did the trick. It skips any null values now and I can skip to the next pagination of my table now. Thanks a lot ❤️

Please or to participate in this conversation.