Summer Developer
Summer Developer

Reputation: 2086

Laravel Nested ForEach Over Eloquent Collection

I'm trying to use a many to many relationship to accomplish the following workflow:

  1. There are many sections and many users. Each user has sections they have unlocked. So for example, if there are two sections (Section I and Section II) the first user, Jim (id = 1), has unlocked Section X, the second user, Debbie (id = 2), has unlocked Section I and II.

  2. To accomplish this, I have three databases, a standard Laravel users, then a sections which stores the section data (id = 1 for Section I and id = 2 for Section II) and then a user_section that I have successfully used as the joint table between the User and Section models. That join table is where users and sections intermingle, if there is an entry for a given user_id in that section, the corresponding section_id is unlocked.

I have the following function which is supposed to 1. Get all the sections for the view and 2. Let me know which of those sections are unlocked by the user.

The problem is I get duplicate Sections showing up, so it will say Section I is unlocked, then Section I is locked all in the same view, this must be in how I am traversing and comparing arrays. With tweaks to the code (where I place a break I can get rid of the duplicates, but then the wrong sections are locked.

My logic is here:

public function getSections(){
    $arrayofuserSections = array();
    $tempArray = array();
    $user = User::where("id",Auth::user()->id)->first();
    foreach ($user->section as $section) {
    $tempArray['Name'] = $section["name"];
    $tempArray['Goals'] = $section["goals"];
    array_push($arrayofuserSections,$tempArray);
}
    $finarray = array();
    $sections=Section::orderBy('order')->get();
    foreach ($sections as $section) {
    foreach($arrayofuserSections as $arraysection){
    if($section->name == $arraysection["Name"])
    {
    $arraysection["Unlocked"] = 1;  
    array_push($finarray,$arraysection);
    }
    else{
    $arraysection["Unlocked"] = 0;
    $arraysection["Name"] = $section->name;
    $arraysection["Goals"] = "";
    array_push($finarray,$arraysection);
    }
    break;
    }
    }
    return $finarray;
}

$user->section is derived from a method on the User model, here:

public function section()
    {
        return $this->belongsToMany('App\Models\Section','user_section')->withTimestamps();
    }

My seeding for the user Debbie is here:

        DB::table('user_section')->insert([
            'user_id' => 2,
            'section_id'=>1
        ]);
        DB::table('user_section')->insert([
            'user_id' => 2,
            'section_id'=>2
        ]);

And yet I get the following result when logged in as Debbie:

So even though Debbie has both sections in the join table, she gets only has one of them unlocked, and again this changes if I remove or move the break around.

Console Log

Upvotes: 0

Views: 395

Answers (1)

fubar
fubar

Reputation: 17388

I think that you're coming at this from the wrong direction.

If you have set up your relationships correctly, you should be able to access sections from users, and users from sections.

// Find user with sections they have unlocked
$user = User::with('sections')->findOrFail(1);

// Find sections with users who have unlocked them
$section = Section::with('users')->findOrFail(1);

If you approach this problem from the direction of sections, you can do the following:

// Find current user id
$userId = Auth::id();

// Find all sections with users who have unlocked them, limited to only the current user
$sections = Section::with([
        'users' => function ($query) use ($userId) {
            return $query->where('id', $userId);
        }
    ])->get();

This will give you all sections, and eager load the users relation, where that is the current user. Thus, if the users relationship is empty, the current user hasn't unlocked that section.

Upvotes: 1

Related Questions