0

I'm having a really strange issue here. I have a user model (detailed below).

It all works fine until I added the getReportsSharedAttribute function. When this is added, the server freezes and I get:

 PHP Fatal error:  Maximum execution time of 60 seconds exceeded in C:\Users\User\PhpstormProjects\laravel-vue\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasRelationships.php on line 637

more:

exception: "Symfony\\Component\\ErrorHandler\\Error\\FatalError"
file: "C:\\Users\\User\\PhpstormProjects\\laravel-vue\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasAttributes.php"

I thought there was something up with the code, so I ran it manually in a controller and dumped it, it worked fine.

So I tried it as a relation instead of an attribute. Same error.

So then I thought, is it just specific to the ReportingSetAssigned model, so I did another query on another collection, and another, and I still get the timeout error.

I tried another Model, it worked fine, for no apparent reason. Even though there were a lot more records inside. It doesn't seem to be dependant on how many columns are involved in the return. None of my tables have more than 50 records inside, even in the relations.

What's going on here? Is there some limit somewhere?

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Query\Builder;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\DB;
use Laravel\Sanctum\HasApiTokens;
use stdClass;
use Illuminate\Database\Eloquent\SoftDeletes;



class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable, SoftDeletes;
    public $appends = [
        'full_name',
        'profile_photo_thumb',
        'permissions_alt',
        'line_managed_only_id',
        'line_managers_only_id',
        'permissions_meetings_only_id',
        'reports_shared',
    ];

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

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

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

    protected $dates = ['deleted_at'];

    public function permissions(){
        return $this->belongsToMany(Permission::class);
    }

    public function timelineitems(){
        return $this->hasMany(TimelineItem::class);
    }

    public function line_managers(){
        return $this->belongsToMany(User::class,'permissions_lm','user_id','lm_id');
    }

    public function line_managed(){
        return $this->belongsToMany(User::class,'permissions_lm','lm_id','user_id');
    }

    public function permissions_meetings(){
        return $this->belongsToMany(Area::class,'permissions_meetings','user_id','area_id')->withPivot('level');
    }

    public function getPermissionsMeetingsOnlyIdAttribute(){
        return $this->permissions_meetings()->pluck('permissions_meetings.area_id');
    }

    public function permissions_qed(){
        return $this->belongsToMany(Area::class,'permissions_qed','user_id','area_id')->withPivot('level');
    }

    public function permissions_reporting(){
        return $this->belongsToMany(Area::class,'permissions_reporting','user_id','area_id')->withPivot('level');
    }

    public function permissions_reporting_sets(){
        return $this->belongsToMany(ReportingSet::class,'permissions_reporting_sets','user_id','set_id')->withPivot('level');
    }

    public function improvement_category_objective_action_milestones(){
        return $this->belongsToMany(ImprovementSetCategoryObjectiveActionMilestone::class);
    }

    public function planning_review_forms(){
        return $this->hasMany(PerformanceManagementSetAssigned::class)->whereHas('set', function($q) {
            $q->where('appraisal', 0);
        });
    }

    public function appraisal_forms(){
        return $this->hasMany(PerformanceManagementSetAssigned::class)->whereHas('set', function($q) {
            $q->where('appraisal', 1);
        });
    }

    public function performance_manager_set_as_lm(){
        return $this->belongsTo(PerformanceManagementSetAssigned::class, 'lm_id');
    }

    public function getPermissionsAttribute(){
        return $this->permissions()->get();
    }

    public function getLineManagersOnlyIdAttribute(){
        return $this->line_managers()->pluck('permissions_lm.lm_id');
    }

    public function getLineManagedOnlyIdAttribute(){
        return $this->line_managed()->pluck('permissions_lm.user_id');
    }

    public function hasPermissionTo($permission){
        if(auth()->user()->super_admin){
            return true;
        }
        if($permission==='super_admin'&&auth()->user()->super_admin){
            return true;
        }
        if(!is_array($permission)){
            $access = $this->permissions()->where('permission', $permission)->exists();
            if($access){
                return true;
            }
            return false;
        }else{
            foreach($permission as $p){
                $access = $this->permissions()->where('permission', $permission)->exists();
                if($access){
                    return true;
                }
            }
        }
    }

    public function checkPermissionReportingSet($permission){
        $access = $this->permissions_reporting_sets()->where('set_id', $permission)->first();
        if($access){
            if($access->pivot->level=='read'){
                return 'read';
            }
            if($access->pivot->level=='write'){
                return 'write';
            }
        }
    }

    public function checkPermissionReportingArea($permission){
        $access = $this->permissions_reporting()->where('area_id', $permission)->first();
        if($access){
            if($access->pivot->level=='true'){
                return true;
            }
        }
    }

    public function truePermission($permission){
        $access = $this->permissions()->where('permission', $permission)->exists();
        if($access){
            return true;
        }
    }

    public function updateTimeline($type_main,$type_sub,$title,$content,$icon,$color,$link,$relevant_id = null,$user_id = null){
        if(!$user_id){
            $user_id = $this->id;
        }

        $t = new TimelineItem();
        $t->type_main = $type_main;
        $t->type_sub = $type_sub;
        $t->title = $title;
        $t->content = $content;
        $t->icon = $icon;
        $t->color = $color;
        $t->link = $link;
        $t->user_id = $user_id;
        $t->relevant_id = $relevant_id;

        $t->save();
    }
    public function getPermissionsForVueAttribute(){
        $permissions = $this->permissions;
        $new = [];
        foreach($permissions as $p){
            $new[$p->permission] = true;
        }

        $new['meeting_areas'] = [];
        $permissions = $this->permissions_meetings;
        foreach($permissions as $p){
            $new['meeting_areas'][$p->id] = $p->pivot->level;
        }

        $new['qed_areas'] = [];
        $permissions = $this->permissions_qed;
        foreach($permissions as $p){
            $new['qed_areas'][$p->id] = $p->pivot->level;
        }

        $new['reporting_areas'] = [];
        $permissions = $this->permissions_reporting;
        foreach($permissions as $p){
            $new['reporting_areas'][$p->id] = $p->pivot->level;
        }

        $new['reporting_sets'] = [];
        $permissions = $this->permissions_reporting_sets;
        foreach($permissions as $p){
            $new['reporting_sets'][$p->id] = $p->pivot->level;
        }

        return json_encode($new);
    }
    public function getPermissionsAltAttribute(){
        //General permissions
        $permissions = Permission::get();
        $newP = [];
        foreach($permissions as $p){
            $newP[$p->permission] = false;
        }

        $permissions = $this->permissions;
        foreach($permissions as $p){
            $newP[$p->permission] = true;
        }

        $newP['meeting_areas'] = [];
        $newP['qed_areas'] = [];
        $newP['reporting_areas'] = [];
        $newP['reporting_sets'] = [];

        foreach(Area::orderBy('name', 'ASC')->get() as $p){
            $newP['meeting_areas'][$p->id] = "false";
            $newP['qed_areas'][$p->id] = "false";
            $newP['reporting_areas'][$p->id] = "false";
        }

        $meetings = DB::table('permissions_meetings')->where('user_id', '=', $this->id)->get();
        foreach($meetings as $p){
            $newP['meeting_areas'][$p->area_id] = $p->level;
        }
        $qed = DB::table('permissions_qed')->where('user_id', '=', $this->id)->get();
        foreach($qed as $p){
            $newP['qed_areas'][$p->area_id] = $p->level;
        }
        $reporting = DB::table('permissions_reporting')->where('user_id', '=', $this->id)->get();
        foreach($reporting as $p){
            $newP['reporting_areas'][$p->area_id] = $p->level;
        }

        foreach(ReportingSet::orderBy('name', 'ASC')->get() as $p){
            $newP['reporting_sets'][$p->id] = "false";
        }

        $reporting = DB::table('permissions_reporting_sets')->where('user_id', '=', $this->id)->get();
        foreach($reporting as $p){
            $newP['reporting_sets'][$p->set_id] = $p->level;
        }

        return $newP;
    }
    public function getCyclesAttribute(){
        return Cycle::orderBy('id')->get();
    }
    public function getFullNameAttribute(){
        return $this->first_name . " " . $this->last_name;
    }
    public function getProfilePhotoThumbAttribute(){
        if($this->profile_photo){ return "THUMB-" . $this->profile_photo; }else{ return "no-avatar.png"; }
    }
    public function getReportsSharedAttribute(){
        return ReportingSet::where('observee_id', $this->id)->where('observee_share', 1)->where('published', 1)->without('set.modules')->get()->toArray();
    }

    public function canLineManage($id){
        if($this->super_admin==1) return true;
        foreach($this->line_managed as $lm){
            if($lm->id==$id){
                return true;
            }
        }
    }

}

EDIT: If I run this code in a controller, it does't hang at all. It loads up the data in less than a second EDIT: Restarted computer, still happening

user3274489
  • 186
  • 2
  • 4
  • 15
  • "minimal reproducible example", not your whole codebase... please post just the query you are running, and the code relevant for the case – Alberto Sinigaglia Mar 17 '22 at 10:59
  • It is not a certain query, it seems to be any query – user3274489 Mar 17 '22 at 11:07
  • How many records are you trying to return? How many records are in the users table? It shouldn't hang if you aren't accessing it. Show your eloquent of how you're trying to access. If you're running a loop over all those functions I can see why it would hang. You have a ton going on there. Example I have a function that I process about 400k rows of data, it usually takes about 20 min to complete. I did have to adjust my timeouts on the server and also bump my memory up a bit. –  Apr 21 '22 at 16:01

1 Answers1

0

This looks a lot like an infinitive call-loop, please refer to this on github issue Allowed Memory size exhaused when accessing undefined index in toArray

You are just running out of memory when calling parent::toArray(). You either need to reduce the amount of items in your collection or increase your allowed memory allocation.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
WardNsour
  • 313
  • 1
  • 2
  • 16
  • Hi WardNsour! Thanks for the info. There is only around 20 items in the collection, nothing massive at all. – user3274489 Mar 17 '22 at 11:08
  • Also - the problem occurs when using toArray and when not. And not on this specific collection, it happens on most. I tried another Model, it worked fine, for no apparent reason. Even though there were a lot more records inside. It doesn't seem to be dependant on how many columns are involved in the return. – user3274489 Mar 17 '22 at 11:18
  • try to add ``->take(10)`` for example, please – WardNsour Mar 17 '22 at 12:06
  • Turns out I was in an infinite loop somehow – user3274489 Mar 17 '22 at 12:26
  • Can you please show the code that got you in an infinite loop, how did this happen and help the stack overflow community to find your answer helpful. @user3274489 – WardNsour Mar 17 '22 at 13:58