How to determine the relation based on a table column name?

I’ve been digging through the Wiki, but can’t figure this out. Hopefully it’s simple and I’m just missing it!

Let’s say I have an ActiveRecord model that has the column $tenant_id. I create a relation like so:

public function getTenant()
{
    return $this->hasOne(\common\models\User::className(), ['id' => 'tenant_id']);
}

Easy enough. But in some other code I want to be able to look at any random ActiveRecord model and say “is this property represented by a relation?” Basically, I want to know if I can call a method for the property, but I don’t know the method name.

So for the example above, I may have other code that gets the model and sees $model->tenant_id. If it can determine if tenant_id can be grabbed using $model->getTenant(), it should do that (basically, to grab $model->tenant->name), but otherwise it would just print $model->tenant_id.

How do I know if tenant_id is accessible via a relation and, if so, the method name for that without knowing the details of the class itself?

The simplest way is to subclass every Model from a BaseModel class that has a “getTenant()” method, that returns null by default.

Then, inside every Model (that extends BaseModel) that has getTenant() relation, you have to override “getTenant()” method returning the relations.

Finally, to know if the model supports “getTenant()”, you have only to check if $model->getTenant() returns null or an object.

if “getTenant()” returns null, you can’t get “tenant_id” by a relation.

Hi @dp778899,

I’m a little curious why you want to know it. I never have wondered if a certain ActiveRecord model has a related ActiveRecord model in terms of a relation (tenant in your example), or just a foreign key (tenant_id) to it. Because I usually use Gii’s model generator to get the skeletons of the ActiveRecord models, they usually have all the relations already defined.

softark, to generalize some debug code I use on the console basically. I have a console app that I use to print out models and relations. So let’s say I tell the app “show --model Abc”. Without knowing more, it will print out each property, e.g., location_id. But I’d like it so the app can say "wait, location_id is represented by a hasOne() and I can instead show $abc->location->name instead of $abc->location_id.

I hope that makes sense!

Hi fabriziocaldarelli, that’s not really where I was trying to go as that assumes there is a tenant. The property may be location, gate, sandwich, anything. The idea is my console app won’t know anything about the model it is looking at – it just knows it’s a model that has properties and many of those properties (e.g., location_id, gate_id, sandwich_id) are related to other models via hasOne() or hasMany() relationships.

@dp778899

I see. It makes sense.
You can access BaseActiveRecord::relatedRecords, but I think it is not available until you use the relations. But there must be some way to enumerate the relation names, I hope.

I’m not sure if this is a proper way of doing it, but I found a way to enumerate the attributes and the relations.

The following code snippet is inserted into an “actionView” method of a controller:

    public function actionView($id)
    {
        $model = $this->findModel($id);

        // get the reflection class of the model
        $ref = new \ReflectionClass($model);
        $className = $ref->getName();
        $docComment = $ref->getDocComment();
        // parse the doc comment and get the properties
        $pattern = '/@property\s+([a-zA-Z0-9]+)(\[\])?\s+\$([a-zA-Z0-9_]+)/';
        preg_match_all($pattern, $docComment, $properties, PREG_SET_ORDER);
        // output the information
        echo "<pre>\n";
        echo "className:\n";
        foreach($properties as $prop) {
            echo "   $prop[1]$prop[2] $prop[3]\n";
        }
        echo "</pre>\n";
        exit;

        return $this->render('view', [
            'model' => $model,
        ]);
    }

In the above, $prop[1] represents the type of the property. It can be an integer, a string, a boolean, etc. and also can be a model class.
$prop[2] is whether '' (blank) or '[]', meaning a scholar or an array respectively.
$prop[3] is the name of the property.

Please note that this code expects the doc comment of the model file is properly written and maintained in a standard format like the following:

/**
 * This is the model class for table "slip".
 *
 * @property integer $id
 * @property integer $book_id
 * @property integer $in_out
 * @property string $date
 * @property integer $ser_no
 * @property integer $item_id
 * @property string $abstract
 * @property string $amount
 * @property string $comment
 * @property integer $deleted
 * @property string $created_at
 * @property integer $created_by
 *
 * @property Audit[] $audits
 * @property Book $book
 * @property Item $item
 * @property User $createdBy
 * @property Voucher[] $vouchers
 */
class Slip extends \yii\db\ActiveRecord
{
    ...

Gii’s model generator is nice enough to automatically generate those doc comments including relations.