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

danhorton's avatar

Can not access/figure out custom model attributes

Hi, i'm trying to add an attribute or figure out a way to structure my data model in a different way.

I have a "TeamMember" model attached to a record, which returns the team associated to it. I can access these fine through:

            @foreach($bid->crm->teammember as $teammember)
            {{$teammember->Name}}  {{$teammember->Record2RoleIdName}} {{$teammember->Record2RoleId}} <br />
            @endforeach

I am trying to add an additional collection/attribute to the TeamMember model so I can access/lookup by role name. E.g. $bid->crm->teammember->sales_manager.

I have tried to implement this via attributes added on to the CRM class, i.e.

protected $appends = ['sales_manager'];

....
    public function getSalesManagerAttribute()
    {
        return $this->where('Record2RoleId', '=', 'SM');
    }

However this attribute returnas a Builder instance when returned which I can not figure out:

Builder {#400 ▼
  #query: Builder {#433 ▶}
  #model: CRM {#402 ▶}
  #eagerLoad: array:1 [▶]
  #localMacros: []
  #onDelete: null
  #passthru: array:13 [▶]
  #scopes: []
  #removedScopes: []
}

Any guidance appreciated.

0 likes
9 replies
lostdreamer_nl's avatar
$this->where('Record2RoleId', '=', 'SM');

That will only return a builder yes, I think you wanted this:

$this->where('Record2RoleId', '=', 'SM')->get();

which will give you a collection.

just a quick note: This will run a query for each team member that you try to select.

danhorton's avatar

Thank you

Is there a way that I can do this without 1 query per attribute, as I would be looking to retrieve around 5 users with this and I do not want 5 extra queries.

The whole of the team associated to the record is returned by $bid->crm->teammember.

Would I need to set up a Team model if I wanted to do $bid->crm->team->manager / $bid->crm_team->director etc?

realrandyallen's avatar

Depending on your actual needs you may be better off with a Scope, this way you can just grab whatever role you need from the DB right off the bat:

public function scopeRole($query, $type)
{
     return $query->where('Record2RoleId', $type);
}

This would allow you to do this in your controllers:

$saleManagers = CRM::role('SM')->get();

https://laravel.com/docs/5.7/eloquent#local-scopes

lostdreamer_nl's avatar

Well, it looks to me there is actualy no relation between the 2.

Right now, all team members have the same manager(s)

All TeamMembers with 'Record2RoleId' = 'SM';

So why not simply get the 2 in 2 different collections?

// controller
$bid = Bid::with('crm.teammembers');
$salesManagers = TeamMember::where('Record2RoleId', 'SM')->get();

return view('some-view', compact('bid', 'salesManagers '));

// in the view:
@foreach($bid->crm->teammember as $teammember)
            {{$teammember->Name}}  {{$teammember->Record2RoleIdName}} {{$salesManagers->pluck('name')->implode(', ') }} <br />
@endforeach

Since there will / can be multiple team members with SM as a role, I'm doing a pluck('name')->implode(', ') to get a comma separated list of their names.

danhorton's avatar

Hi, thanks for your detailed replies.

There is no direct relationship from Bid<-> TeamMember. They are related on Bid->CRM->TeamMember.Does a hasManyThrough relationship need to be set up?

The data returned for TeamMember looks as below

Record2IdName   Record2Id   rolename
glq qkoq    11FA6105-A015-E111-1C11-00505611001N    Project Director
glq qkoq    11FA6105-A015-E111-1C11-00505611001N    Team Member
mooqm qqrqmxxorf    56E50EN1-1F15-E111-1C11-00505611001N    Project Manager
mqkof fqqgqqrx mgroqqmqg    QF117F0C-EACA-E711-10E5-005056N11N1F    Project Quality Leader
mqkof fqqgqqrx mgroqqmqg    QF117F0C-EACA-E711-10E5-005056N11N1F    Team Member
qgr Lqmxoolq Lqrqgm Q1A0111N-1E15-E111-1C11-00505611001N    Account Director
qgr Lqmxoolq Lqrqgm Q1A0111N-1E15-E111-1C11-00505611001N    Sales Director
qgr Lqmxoolq Lqrqgm Q1A0111N-1E15-E111-1C11-00505611001N    Team Member
qqcoqgl fomx    1111QF6A-711N-E111-A1N1-00505611001N    Sales Manager
qqcoqgl fomx    1111QF6A-711N-E111-A1N1-00505611001N    Team Member
qqcoqgl fomx    1111QF6A-711N-E111-A1N1-00505611001N    Team Member
qqcoqgl q qgqgrqgm  11A1661C-A015-E111-1C11-00505611001N    Project Quality Reviewer
qqqg Olg Vqlqgm A11Q1111-1F15-E111-1C11-00505611001N    Sales Manager
qqqg Olg Vqlqgm A11Q1111-1F15-E111-1C11-00505611001N    Sales Support
xqm oorqom  1110FQ17-717N-E111-10F1-005056N11QQ1    Team Member

I'd like to be able to have the rolename an Record2IdName accessible via $bid->crm->team->manager / $bid->crm_team->director or something similar. Would scope be the best way t do this?

or is there a way in the controller to pass $team = TeamMember::where('Record2RoleId', '11A1661C-A015-E111-1C11-00505611001N'); multiple times for different types of roles?

danhorton's avatar

@REALRANDYALLEN - Hi, if i put this on the TeamMember model (where i guess it should go, as that is where the Record2RoleId column is sourced from, it retrieves everything from the TeamMember model with no relation to the CRM or Bid Model.

            $team['bid_manager'] = TeamMember::role('SM')->get();
select * from [Connection] where [Record2RoleId] = SM and [record2objecttypecode] = 8 and [record1objecttypecode] = 3 and [connection].[statuscode] = 1

This is the teamMember model

    public function crm()
    {
        return $this->belongsTo('App\CRM', 'Record1Id', 'OpportunityId');
    }

    public function scopeRole($query, $type)
    {
        return $query->where('Record2RoleId', $type);
    }

CRM Model

    public function bid()
    {
        return $this->belongsTo('App\Bid');
    }

    public function teammember()
    {
        return $this->hasMany('App\TeamMember', 'Record1Id', 'OpportunityId');
    }

Bid model

    public function crm()
    {
        return $this->hasOne('App\CRM', 'New_SalesNumber', 'crm_ref')->with('teammember');
    }

    public function teammember()
    {
        return $this->hasManyThrough('App\TeamMember', 'App\CRM', 'new_salesnumber', 'Record1Id', 'crm_ref', 'opportunityid');
    }
realrandyallen's avatar

You could eager load that data if you need it:

https://laravel.com/docs/5.7/eloquent-relationships#eager-loading

$team['bid_manager'] = TeamMember::role('SM')->with('crm')->get();

Alternatively you can automatically load this data every time on the model by using the $with variable at the top of your model:

namespace App;

use Illuminate\Database\Eloquent\Model;

class TeamMember extends Model
{
    ...

    protected $with = [
        'crm',
    ];

    ...
danhorton's avatar

Thanks for the replies.

I have sort of managed to get this to work via a method which seems totally backwards and dirty. The only problem with it is that this creates 1 query per field retrieved. I eventualy need to pull more fields using the role so I figure this will end up creating tons of extra queries:

Normal query

select * from [Connection] where [Connection].[Record1Id] in ('15AGA3494-7AE5-E711-834E6-343453456B82B3F', '23334424D-FABE-E711-834E4-343453456B82B3F', '679D6E15-F247-E511-AEE6-343453456924777', '68A4558F-E91A-E811-834E9-343453456B82B3F', '68C747C1-92EF-E511-834D2-343453456B82DD8', '6D3A1EE9-59FF-E611-834E2-343453456B82DD8', 'A762675D-233434-E711-834DC-343453456B82B3F', 'AE814366-7FE7-E111-8AFD-343453456924777', 'C18A83F9-2363-E811-834F2-343453456B82DD8', 'CA41A39A-2663-E811-834F2-343453456B82DD8') and [record2objecttypecode] = '8' and [record1objecttypecode] = '3' and [connection].[statuscode] = '1'

Repeated query example

select * from [Connection] where [Record2RoleIdName] = 'Bid Manager' and [Record1Id] = '23334424D-FABE-E711-834E4-343453456B82B3F' and [record2objecttypecode] = '8' and [record1objecttypecode] = '3' and [connection].[statuscode] = '1'

The query is basically the same so i'm sure these can be ingratiated somehow.

BidController:

            $bids = Bid::where('mandatory', 1)->with('crm')->paginate(10);
            foreach($bids as $b)
            {  
                $team[$b->crm_ref]['Bid_Manager'] = TeamMember::role('Bid Manager', $b->crm->OpportunityId);
            }
            
            return view('public.bids.mandatory.index', compact('bids', 'team'));

I can then access this in the view via

$team[$bid->crm_ref]['Bid_Manager']. 

I am using the following scope in the TeamMember model:

    public function scopeRole($query, $type, $opportunity)
    {
        return $query->where('Record2RoleIdName', $type)->where('Record1Id', $opportunity)->get();
    }

is there a better way of doing this?

danhorton's avatar
danhorton
OP
Best Answer
Level 3

I ended up solving this by using this in the view

$bid->crm->teammember->where('Record2RoleIdName', 'Bid Manager')->pluck('Name')->implode(', ')}}

CRM Model

    public function teammember()
    {
        return $this->hasMany('App\TeamMember', 'Record1Id', 'OpportunityId');
    }

TeamMember Model

    public function crm()
    {
        return $this->belongsTo('App\CRM', 'Record1Id', 'OpportunityId');
    }

Please or to participate in this conversation.