Get a field value from using 3 different tables in YII2

I have an aggregators table

below are the fileds in the aggregators table
(id,agent_id)

I have a transactions table

below are the fileds in the transactions table
(id,agent_id)

I have an agents table

below are the fileds in the agents table
(id,name)

So what i want to do is display the agents name of a particular aggregator inside the transaction gridview. So inside the transactions index.php view i want to have something like this:

<?=
    GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'tableOptions' => ['class' => 'table table-sm  table-bordered'],
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],
[
    'attribute' => 'agent.aggregators.name',
    'label' => 'Aggregator\'s  Name',
],
]

Please how do i do this.

So, the relevant models are Aggregator, Transaction and Agent, right?
And the relations among them are:

  • Aggregator has one Agent, and Agent has many Aggregators … Aggregator (N) – Agent (1)
  • Transaction has one Agent, and Agent has many Transactions … Transaction (N) – Agent (1)

Am I right? If I’m right then:

  • Transaction has many Aggregators through Agent … Transaction – Agent (1) – Aggregator (N)

So, you would be able to do something like the following:

In Agent.php

public function getAggregatorsNames()
{
    $names = [];
    foreach($this->aggregators as $aggregator) {
        $names[] = $aggregator->name;
    }
    return implode(", ", $names);
}

And in the view:

[
    'attribute' => 'agent.aggregatorsNames',
    'label' => 'Aggregators\'  Names',
],
1 Like

@softark thanks for your response and thanks for understanding my question.

But the thing is i’m trying to display the aggregators name inside the transactions gridview like i stated in my question the view being referred to here is the transaction view.

[
‘attribute’ => ‘agent.aggregatorsNames’,
‘label’ => ‘Aggregators’ Names’,
],

For example, the following will show the name of the Agent of the Transaction.

[
    'attribute' => 'agent.name',
    'label' => 'Agent\'s Name',
],

And if Agent had had one Aggregator instead of many Aggregators, then you could have been able to write like this:

[
    'attrubute' => 'agent.aggregator.name',
    'label' => 'Aggregator\'s Name',
],

But in fact Agent has many Aggregators. So you have to have an attribute in Agent model that represents the names of the aggregators, which you could implement as a getter method named getAggregatorsNames.

1 Like

Or, you could implement the getter method in Transaction model instead:

In Transaction.php

public function getAggregatorsNames()
{
    $names = [];
    foreach($this->agent->aggregators as $aggregator) {
        $names[] = $aggregator->name;
    }
    return implode(", ", $names);
}

And in the view:

[
    'attribute' => 'aggregatorsNames',
    'label' => 'Aggregators\'  Names',
],

But the getter method in Agent would be more reasonable.

1 Like

When i applied this, i get this error.

“Trying to get property of non-object”

in C:\xampp\htdocs\keshexpress-web-app\library\yii-kesh-express-app\models\Transactions.php at line 123

Line 123 is this part of the code:

foreach($this->agents->aggregators as $aggregator) {

Are your model names Agent, Aggregator and Transaction? Are they not Agents, Aggregators and Transactions? If so, you have to adjust the code according to the definitions of the relations.

foreach($this->agents->aggregatorss as $aggregator) {

BTW, the following wiki is a must read. Please check it.
https://www.yiiframework.com/wiki/227/guidelines-for-good-schema-design

1 Like

Yes my model names are Agents , AgentsAggregator and Transactions. Already made the corretion and now i get this error:

Unknown Property – yii\base\UnknownPropertyException

Getting unknown property: app\models\Transactions::agents

I appreciate your help.

Have you defined the relations among them?

In Transactions.php:

public function getAgents()
{
    return $this->hasOne(Agents::className(), ['id' => 'agent_id']);
}

In Agents.php

public function getAggregatorss()
{
    return $this->hasMany(Aggregators::className(), ['agent_id' => 'id']);
}

Those getter methods define special attributes (properties) Transaction::agents and Agents::aggregatorss that will retrieve the related models.

Please check the following section of the guide:
https://www.yiiframework.com/doc/guide/2.0/en/db-active-record#relational-data

1 Like

So sorry this is an inherited project, please permit my mistakes. the name of the aggregator model is actually AgentsAggregator and not Aggregators.

Apparently i think the “Trying to get property of non-object” error is coming from this code:

foreach($this->agents->aggregators as $aggregator) {

to be specific its coming from here: $this->agents->aggregators

after providing you with the right name of the model please what do you think should be correct way to write this $this->agents->aggregators?

yes the relations have been defined.

The name of a relation property is made from the name of its relation method.
For example, “getAgents()” method will define “agents” relation.

What relation methods do Transactions and Agents have?

In my transaction model i have getAgent() relation.

OK, then the name of the relation property is “agent”.
And what relation method do you have in your Agents model? “getAgentsAggregators()” or something like that?

Yes you are right it is getAgentsAggregators().

OK, then the following should work:

    foreach($this->agent->agentsAggregators as $aggregator) {

Please take your time to read the following section of the guide.
Guide > Active Record > Working with Relational Data
https://www.yiiframework.com/doc/guide/2.0/en/db-active-record#relational-data

1 Like

@softark thanks for your patience.i just went through the code thoroughly, what i actually want to show is the agent name of that particular aggregator.

No problem.

But as long as you are working with transactions, you don’t need to retrieve the aggregators to get “the agent name”, because a transaction has only one agent.

okay. So if i get you right i should just do this: agent.name and i should be fine?