Yii2: Multiple inverse relations to same model?

322 views Asked by At

How do I handle multiple inverse relations pointing to the same active record? For example:

class Bicycle extends ActiveRecord {

    public function getFrontWheel() {
        return $this
            ->hasOne(Wheel::class, ['id' => 'front_wheel_id'])
            ->inverseOf('bicycles');
    }

    public function getRearWheel() {
        return $this
            ->hasOne(Wheel::class, ['id' => 'rear_wheel_id'])
            ->inverseOf('bicycles');
    }

}

class Wheel extends ActiveRecord {

    public function getBicycles() {
        return $this
            ->hasMany(Bicycle::class, ['???' => 'id'])
            ->inverseOf('??????');
    }

}

What can I do here? I critically need the inverse relations.

2

There are 2 answers

0
mae On BEST ANSWER

Here is my own solution. Key points:

  • It all boils down to proper naming.
  • Inverse relations are bijective! In other words, every relation always has to have its own unique mirror relation on the other end.
class Bicycle extends ActiveRecord {

    public function getFrontWheel() {
        return $this
            ->hasOne(Wheel::class, ['id' => 'front_wheel_id'])
            ->inverseOf('frontWheelBicycles');
    }

    public function getRearWheel() {
        return $this
            ->hasOne(Wheel::class, ['id' => 'rear_wheel_id'])
            ->inverseOf('rearWheelBicycles');
    }

}
class Wheel extends ActiveRecord {

    public function getFrontWheelBicycles() {
        return $this
            ->hasMany(Bicycle::class, ['front_wheel_id' => 'id'])
            ->inverseOf('frontWheel');
    }

    public function getRearWheelBicycles() {
        return $this
            ->hasMany(Bicycle::class, ['rear_wheel_id' => 'id'])
            ->inverseOf('rearWheel');
    }

}
5
Papp Péter On

i would suggest to do the following:

create two new classes:

  • class FrontWheel extends Wheel {
  • class RearWheel extends Wheel {

in new classes you can set easily the relation.

How to instantiate the correct class? There is a method in ActiveRecord instantiate() where you can write your logic which wheel class need to be created.

class Wheel extends ActiveRecord {
...

  public static function instantiate ( $row ) {
      if($row['type'] === 'RearWheel') {
           return new RealWheel();
      }
      ...

  }

full code:

class Bicycle extends ActiveRecord
{

    public function getFrontWheel()
    {
        return $this
            ->hasOne(Wheel::class, ['id' => 'front_wheel_id'])
            ->inverseOf('bicycles');
    }

    public function getRearWheel()
    {
        return $this
            ->hasOne(Wheel::class, ['id' => 'rear_wheel_id'])
            ->inverseOf('bicycles');
    }

}

abstract class Wheel extends ActiveRecord
{

    public static function instantiate($row)
    {
        if ($row['type'] === 'RearWheel') {
            return new RealWheel();
        }
        if ($row['type'] === 'FrontWheel') {
            return new FrontWheel();
        }
        
        throw new InvalidConfigException();
    }

    abstract public function getBicycles();
}

class RealWheel extends Wheel
{

    public function getBicycles()
    {
        return $this
            ->hasMany(Bicycle::class, ['rear_wheel_id' => 'id'])
            ->inverseOf('rearWheel');
    }

}

class FrontWheel extends Wheel
{

    public function getBicycles()
    {
        return $this
            ->hasMany(Bicycle::class, ['front_wheel_id' => 'id'])
            ->inverseOf('frontWheel');
    }

}