0

Context

I have 3 migrations:

Members table:

<?php   

class CreateMembersTable extends Migration
{    
    public function up()
    {
        Schema::create('members', function (Blueprint $table) {
     
             $table->increments('id');
             $table->string('name')->unique();
             $table->timestamps();
        });
    }    
}

Member Link Types table:

<?php

class CreateMemberLinkTypesTable extends Migration
{
    public function up()
    {
        Schema::create('member_link_types', function (Blueprint $table) {
            
            $table->string('name', 50)->primary()->unique();
            $table->string('category', 50)->index();
            $table->string('description', 50)->unique();
            $table->string('description_on_male', 50);
            $table->string('description_on_female', 50);
            $table->string('reverse_link', 50);
    });

    Artisan::call('db:seed', [
        '--class' => MemberLinkTypeSeeder::class
    ]);

    Schema::table('member_link_types', function ($table) {

        $table->foreign('reverse_link')->references('name')->on('member_link_types');
    });
}

And, member links table:

<?php

class CreateMemberLinksTable extends Migration
{

    public function up()
    {
        Schema::create('member_links', function (Blueprint $table) {

            $table->increments('id');
            $table->string('member_link_type', 50)->index();
            $table->integer('from_member_id')->unsigned()->index();
            $table->integer('to_member_id')->unsigned()->index();
            $table->timestamps();
            $table->foreign('member_link_type')->references('name')->on('member_link_types');
            $table->foreign('from_member_id')->references('id')->on('members');
            $table->foreign('to_member_id')->references('id')->on('members');
        });
    }

}

After seed member_link_types table, it look like this:

name   | category | description | description_on_male| description_on_female| reverse_link |
-------|----------|-------------|--------------------|----------------------|--------------|
child  | family   | Child       | son                | daughter             | parent |
parent | family   | Parent      | dad                | mon                  | child |
sibling| family   | Sibling     | brother            | sister               | sibling |
spouse | family   | Spouse      | husband            | wife                 | spouse |

So, I define the relationships at Member Link Class model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class MemberLink extends Model
{        

    protected $with = [
        'from', 'to', 'type'
    ];

    public function from()
    {
        return $this->belongsTo('App\Member', 'from_member_id');
    }

    public function to()
    {
        return $this->belongsTo('App\Member', 'to_member_id');
    }

    public function type()
    {
        return $this->belongsTo('App\MemberLinkType', 'member_link_type', 'name');
    }    
    
}

The problem

Given that I have the table 'member_links' like this:

| id | member_link_type | from_member_id | to_member_id |
|----|------------------|----------------|--------------|
| 1  | parent           |      1         |       2      | 

When I load the model

$link = MemberLink::find(1)

I got this:

App\MemberLink {#3012
     id: 1,
     member_link_type: "parent",
     from_member_id: 1,
     to_member_id: 2,
     from: App\Member {#3024
       id: 1,
       name: "Micaela Beltrão",       
     },
     to: App\Member {#3022
       id: 2,
       name: "Ian Fernandes",       
     },
     type: null,
   }

That is, the to and from properties were loaded successfully, but the type relationship did not (null).

However, if I try to get to the relationship directly from the object I get a different result:

$link = MemberLink::find(1);
$link->belongsTo('App\MemberLinkType', 'member_link_type', 'name')->get()

Giving this:

=> Illuminate\Database\Eloquent\Collection {#3013
 all: [
   App\MemberLinkType {#3032
     name: "parent",
     category: "family",
     description: "Parent",
     description_on_male: "dad",
     description_on_female: "mom",
     reverse_link: "child",
   },
 ],

}

How get type relationship to be loaded?

I'm using Laravel 5.6.

Update 1

Following Aarón Gutiérrez sugestion I add a Surrogate Key (integer column) as Key of member_link_types table, And after making the necessary changes:

$link = MemberLink::find(1)
=> App\MemberLink {#3019
     id: 1,
     member_link_types_id: 1,
     from_member_id: 11,
     to_member_id: 12,
     created_at: "2018-08-14 14:28:46",
     updated_at: "2018-08-14 14:28:46",
     from: App\Member {#3031
       id: 11,
       name: "Simon Ferminiano",           
       created_at: "2018-08-14 14:24:49",
       updated_at: "2018-08-14 14:24:49",
     },
     to: App\Member {#3029
       id: 12,
       name: "Luciano Leon",
       
       created_at: "2018-08-14 14:24:52",
       updated_at: "2018-08-14 14:24:52",
     },
     type: App\MemberLinkType {#3030
       id: 1,
       name: "parent",
       category: "family",
       description: "Parent",
       description_on_male: "dad",
       description_on_female: "mom",
       reverse_link: 2,
     },

So, I suppose there is some limitation when I use the text field as the table's primary key, would it?

Community
  • 1
  • 1
  • I wonder if `type` is reserved in some capacity. Can you try a different name for that relationship? Something like `public function linkType()`, and adjust your `protected $withs ...` to match. – Tim Lewis Aug 14 '18 at 14:34
  • I tried this but I did not succeed. – Marcos Freire Aug 14 '18 at 16:51
  • Hmm... At a glance, everything looks ok. If you run `dd(MemeberLink::find(1)->type)`, what do you get? – Tim Lewis Aug 14 '18 at 16:52

3 Answers3

0

What's the purpose on your specific case for using a VARCHAR data type field as foreign key?

The problem with VARCHAR being used for any KEY is that they can hold WHITE SPACE. White space consists of ANY non-screen-readable character, like spaces tabs, carriage returns etc. Using a VARCHAR as a key can make your life difficult when you start to hunt down why tables aren't returning records with extra spaces at the end of their keys.

As stated here: VARCHAR as foreign key/primary key in database good or bad?

So, that could be a problem.

Aarón Gutiérrez
  • 1,400
  • 3
  • 16
  • 41
0

By default, only the model keys will be present on the pivot object. If your pivot table contains extra attributes, you must specify them when defining the relationship:

return $this->belongsTo('App\MemberLinkType')->withPivot('member_link_type');

from https://laravel.com/docs/5.6/eloquent-relationships

EDIT sry did not see that it was not a pivot, have you tried.

$link->belongsTo('App\MemberLinkType', 'member_link_type', 'name')->with('type');
Marcus
  • 1,850
  • 1
  • 12
  • 24
  • `belongsTo` is not a many-to-many relationship, so there's no pivot. – Tim Lewis Aug 14 '18 at 14:26
  • My mistake! This looked like a pivot table to me | id | member_link_type | from_member_id | to_member_id | | 1 | parent | 1 | 2 | – Marcus Aug 14 '18 at 14:28
  • It looks like one, but you can see that `MemberLink` is a Model for that table, which he is querying directly. Either way, I'm pretty sure using `->belongsTo()` with `->withPivot()` will fail (or at least not behave as expected) – Tim Lewis Aug 14 '18 at 14:31
  • Aha, the more you know ;) – Marcus Aug 14 '18 at 14:32
0

I was able to solve. It was necessary to set the $incrementing property to false in the MemberLinkType model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class MemberLinkType extends Model
{
    public $timestamps = false;    
    public $incrementing = false;    
    protected $primaryKey = 'name';    

}

After that, I got what I wanted:

$link = MemberLink::find(1)
=> App\MemberLink {#3071
     id: 1,
     member_link_type: "parent",
     from_member_id: 11,
     to_member_id: 12,
     created_at: "2018-08-14 15:03:22",
     updated_at: "2018-08-14 15:03:22",
     from: App\Member {#3065
       id: 11,
       name: "Gabriela Espinoza",           
     },
     to: App\Member {#3067
       id: 12,
       name: "Felipe Assunção",           
     },
     type: App\MemberLinkType {#3066
       name: "parent",
       category: "family",
       description: "Parent",
       description_on_male: "dad",
       description_on_female: "mom",
       reverse_link: "child",
     },
   }