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

ntbutler-nbcs's avatar

Help with Inconsistent result in a model method

G'day all!

I'm still relatively new to Laravel (have been developing with it for around a year now), so I'm guessing that my issue below would come from a misunderstanding for how to approach model relations. My issue is intermittent though, so I wasn't sure if it might be a database performance issue instead.

I am building out a "polls" feature for an internal portal in a school. The polls essentially are a "course survey" like you might take at the end of a unit, giving feedback on the instructor and course content, so there may be multiple "polls" that are active, and a user should be able to response to the poll if it's active and they haven't already responded to it for that class.

On the homepage of the site, I want to display a link/button for the students to click if there are available polls that are awaiting a response from them. So in the blade template I essentially have this:

@if ($user->hasUnansweredPolls())
    <a class="tile-link" href="{{ route('feedback.show.available') }}">Provide feedback on your classes</a>
@endif

Now this is working, but not 100% of the time. Sometimes, the link doesn't appear when the blade template is rendered. However if I refresh the page, it does appear. If I sit there and refresh the page over and over, most of the time the link renders, but some of the time it doesnt.

So my main question - would this likely be a performance issue, or something else?

Further Context

For various reasons, the model structure is a little complex in the backend, which is giving me some flexibility for how the polls are used and re-used in the future.

One crucial point is that a poll isn't directly scoped to a user, so there is a bit of processing to identify:

  • What classes is the user enroled in
  • Do any of those classes have any open polls currently
  • Has the user already responded to any of those polls

On the User model, I've added a the "hasUnansweredPolls()" method to try to process this. The method will either return an array of class codes (e.g ["7SCI1", "7MATH4"]) for any polls that they still need to respond to, or will return false. My problem is that the method sometimes returns false even when there are unanswered polls.

E.g. if I simply had the following in the blade template, sometimes I get "false", and then I refresh and I get '["7SCI1", "7MATH4"]'

dd($user->hasUnansweredPolls());

So my thinking here is that I might have a performance issue in the database and it loading the relations properly and so forth, but as I am still relatively new to Laravel, it might be just the way I am trying to process the data in that new method.

I've tried to tidy up and condense the models code below for brevity, so if there's any silly syntax errors then that's probably not the issue as, again, the code does work most of the time. Anyway, essentially the setup is as follows:

/** Model: Poll. In the DB, contains a JSON array of questions, outlining question text, type, validation, etc.. */
class Poll {
    public function responses(): HasMany
    {
        return $this->hasMany(PollResponse::class);
    }
    public function pollingWindows(): HasMany
    {
        return $this->hasMany(PollWindow::class);
    }
}

/** Model: PollWindow. Open a poll to accept responses between specified dates */
class PollWindow {
    public function poll(): BelongsTo
    {
        return $this->belongsTo(Poll::class);
    }
    public function participants(): HasMany
    {
        return $this->hasMany(PollWindowScope::class);
    }
    public function isActive()
    {
        return date_create($this->availableFrom) < date_create() && date_create($this->availableUntil) > date_create();
    }
}

/** Model: PollWindowScope. Assign certain timetabled classes to an open PollWindow */
class PollWindowScope {
    public function window(): BelongsTo
    {
        return $this->belongsTo(PollWindow::class);
    }
    public function responses(): HasMany
    {
        return $this->hasMany(PollResponse::class)->where('scope_id',$this->id);
    }
    public function class_relation(): HasOne
    {
        return $this->hasOne(TimetabledClass::class);
    }
    public function class()
    {
        return $this->class_relation()->first();
    }
    public function isActive()
    {
        return $this->window->isActive();
    }
    public function userHasResponded(User $user)
    {
        return $this->responses()->where('id', $user->id)->exists();
    }
}

/** Model: PollResponse. A JSON array of response data, tied to the questions in the Poll model */
class PollResponse {
    public function poll(): BelongsTo
    {
        return $this->belongsTo(Poll::class);
    }
    public function scope(): BelongsTo
    {
        return $this->belongsTo(PollWindowScope::class);
    }
    public function user(): HasOne
    {
        return $this->hasOne(User::class);
    }

}

/** Model: TimetabledClass. A JSON array of response data, tied to the questions in the Poll model */
class TimetabledClass {
    public function enrolments(): HasMany
    {
        return $this->hasMany(TimetableEnrolment::class);
    }
    public function teachers(): HasMany
    {
        return $this->hasMany(TimetableEnrolment::class)->where('role','teacher');
    }
}

/** Model: TimetableEnrolment. A JSON array of response data, tied to the questions in the Poll model */
class TimetableEnrolment {
    public function timetabled_class(): BelongsTo
    {
        return $this->belongsTo(TimetabledClass::class);
    }
    public function user(): HasOne
    {
        return $this->hasOne(User::class);
    }
    public function isActive()
    {
        return date_create($this->start_date) < date_create() && ($this->end_date === null || date_create($this->end_date) > date_create());
    }
}

class User {
    public function hasUnansweredPolls()
    {
        // Get the user's active class codes
        $classes = TimetableEnrolment::where('id',$this->id)->get();
        $classList = $classes->filter( function ($enrol) { return $enrol->isActive(); })->all();
        $activeClassCodes = array_column( $classList, 'class_code');
        
        // Find any active PollWindowScopes that match the class codes
        $scopes = PollWindowScope::whereIn('timetabled_class',$activeClassCodes)->get();
        // Filter out the scopes that the user has responded to
        $activeScopes = $scopes->filter( function ($scope) { return $scope->isActive() && !($scope->userHasResponded($this)); })->all();
        $activeScopeCodes = array_column( $activeScopes, 'timetabled_class');
        
        if (array_intersect($activeClassCodes, $activeScopeCodes)) {
            return $activeScopes;
        }
        return false;
    }
}
0 likes
0 replies

Please or to participate in this conversation.