SergeAx
SergeAx

Reputation: 542

Yii2 ORM smart get related model

I have 3 Yii2 ActiveRecord models: Emplpoyee, Department and Organization.

Employee must have either one Department or one Organization, that is ensured by validation (fails if both department_id and organization_id are null or both are not null). Department must have one Organization, it's a standard yii2 ORM relation via hasOne().

Gii created this code for employee/organization relation:

class Employee
{
    public function getOrganization()
    {
        return $this->hasOne(app\models\Organization::class, ['id' => 'organization_id']);
    }
}

So when I call $employeeObject->organization, I will get null if organization_id in SQL table is null.

I want to modify this standard getter function to return $this->department->organization in be able to get $employee->organization via magic getter like this: if an employee has department - organization gets from department, otherwise - via standard relation.

Update: if I write:

/**
 * @return ActiveQuery
 */
public function getOrganization()
{
    if (!is_null($this->organization_id)) {
        return $this->hasOne(app\models\Organization::class, ['id' => 'organization_id']);
    } elseif (!is_null($this->department)) {
        return $this->department->getOrganization();
    } else {
        // We should not be here, but what if we are?
    }
}

How do I handle a situation of broken relation to Department or both organization_id and department_id is null? Which ActiveQuery do I return?

Upvotes: 0

Views: 305

Answers (1)

rob006
rob006

Reputation: 22174

Relations definitions are used to build SQL query for related models. In some cases (eager loading, joins) query needs to be created before you get actual object, so you cannot use if ($this->organization_id) because $this is not actual model and $this->organization_id will alway be null. The only thing that you can get is to define regular relations without any conditions and getter method (getOrganizationModel()), which will return correct organization from relations:

class Employee extends ActiveRecord {

    public function getDepartment() {
        return $this->hasOne(Department::class, ['id' => 'department_id']);
    }

    public function getOrganization() {
        return $this->hasOne(Organization::class, ['id' => 'organization_id']);
    }

    public function getOrganizationModel() {
        if ($this->organization_id !== null) {
            return $this->organization;
        }

        return $this->department->organization;
    }
}

class Department extends ActiveRecord {

    public function getOrganization() {
        return $this->hasOne(Organization::class, ['id' => 'organization_id']);
    }
}

Then you can query records with both relations and use getter to get organization:

$employees = Employe::find()->with(['department.organization', 'organization'])->all();
foreach ($employees as $employee) {
    echo $employee->getOrganizationModel()->name;
}

Upvotes: 1

Related Questions