dom_ahdigital
dom_ahdigital

Reputation: 1679

Laravel - how to use belongsToMany relationship using second index and not primary key

App overview

I'm learning Laravel and my dummy app allows users to log issues against projects. A project can have many issues and an issue can have many tags assigned to it.

I'm currently working on projects/show.blade.php which outputs all issues associated with a project in an HTML table with columns for name and tags.

Issue

I've setup 'belongs to many' relationships so that my issues, tags and issues_tags (intermediary) tables can talk to each other but I've run into a problem whereby tags are being output against the wrong issues.

The problem stems from the fact that my issues table (by design) includes an item_id column, which is an index but not the primary key. I use item_id for routing/display on the frontend because I wanted the issue ID that users see to start from 1 for every new project added instead of being sequential and everyone knowing how many there are in the database.

For example, tags assigned to issue #3 are called using issues.id and not issues.item_id.

Sample data

Sample data

What I want: Tag_id's 1, 2 and 3 to be assigned to issue 3 where issues.item_id is the index upon which to match. E.g.

Correct result

What I am getting: Tag_id's 1, 2 and 3 are assigned to issue 3 but are retrieved using issues.id as the index to match against. E.g.

Incorrect result

Code

Issues migration

    Schema::create('issues', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('item_id')->index()->unsigned();
        $table->integer('project_id')->unsigned();
        $table->string('name');
        $table->longText('description');
        $table->tinyInteger('status')->default('1');
        $table->integer('created_by');
        $table->timestamps();
    });

Tags migration

Schema::create('tags', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->timestamps();
});

Issues tags migration

Schema::create('issues_tags', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('tag_id')->unsigned()->index();
    $table->foreign('tag_id')->references('id')->on('tags');
    $table->integer('issue_id')->unsigned()->index();
    $table->foreign('issue_id')->references('item_id')->on('issues');
    $table->timestamps();
});

Issue model

public function tags()
{
    return $this->belongsToMany('App\Tag', 'issues_tags');
}

Tag model

public function issues()
{
    return $this->belongsToMany('App\Issue', 'issues_tags', 'issue_id')->withPivot('item_id');
}

View (projects/show.blade.php)

  <table>
    <tr>
      <th>ID</th>
      <th>Item ID</th>
      <th>Issue</th>
      <th>Tags</th>
    </tr>
    @foreach($issues as $issue)
    <tr>
      <td>{{ $issue->id }}</td>
      <td>{{ $issue->item_id }}</td>
      <td>{{ $issue->name }}</td>
      <td>
        @foreach($issue->tags as $tag)
          <span class="badge badge-primary">{{ $tag->name }}</span>
        @endforeach
      </td>
    </tr>
    @endforeach
  </table>

Question

How can I use issues.item_id instead of issues.id when using a belongsToMany relationship in Laravel?

Upvotes: 0

Views: 4832

Answers (2)

Kamlesh Solanki
Kamlesh Solanki

Reputation: 646

Here I have put the model relationship code for the issues tags model

<?php

namespace App\Models;
use Eloquent;

class IssueTag extends Eloquent{

    protected $table = 'issues_tags';

    public function issues()
    {
        return $this->belongsTo('App\Models\Issue', 'issue_id', 'item_id');
    }

    public function tags()
    {
        return $this->belongsTo('App\Models\Tag', 'tag_id');
    }

}

Upvotes: 0

Nutic
Nutic

Reputation: 1437

From source code:

/**
     * Define a many-to-many relationship.
     *
     * @param  string  $related
     * @param  string  $table
     * @param  string  $foreignPivotKey
     * @param  string  $relatedPivotKey
     * @param  string  $parentKey
     * @param  string  $relatedKey
     * @param  string  $relation
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
                                  $parentKey = null, $relatedKey = null, $relation = null)

You can override any params, Laravel just puts defaults made from table names in there if you do not specify which one to use.

In your case it would be:

In Model/Issue.php

public function tags() {
  return $this->belongsToMany(Tag::class, 'tag_id', 'issue_id');
}

In Model/Tag.php

public function issues() {
  return $this->belongsToMany(Issue::class, 'issue_id', 'tag_id');
}

Upvotes: 1

Related Questions