mDIS and Yii2 PHP Classes

full inheritance hierarchy

Templates-Manager page
Developer page

PHP Class hierarchy

The following paragraphs explain how an mDIS model object is actually implemented: via several PHP classes in an elaborate hierarchy.

The PHP files mentioned here can be found in the backend/models directory, and in various backend/vendor/yiisoft/yii2/ subdirectories.

Let's explain the Class hierarchy using the Core entity as example.

An mDIS PHP class is named CoreCore because table Core is part of the Core tableset. The associated database-table would be named core_core.

# mDIS PHP Class Hierarchy
# Model Classes (and 1 Form Class)
  yii\db\ActiveRecord                   # large parent class, provided by Yii
   app\models\base\Base                 # "Handwritten" Base Class
    app\models\base\BaseCoreCore        # implements linked table 'core'
     app\models\CoreCore                # add own behaviors here (optional)
       app\forms\CoresForm\             # has only 1 method: scenarios()

# Search Classes
     app\models\base\BaseCoreCoreSearch # inherits from BaseCoreCore (!)
       app\models\CoreCoreSearch        # empty class, no methods on purpose
         app\forms\CoreCoreSearch       # empty class, no methods on purpose
1
2
3
4
5
6
7
8
9
10
11
12

In the code block above, the indentation means "extends" or "inherits".

At the top, there is a large base class yii\db\ActiveRecord, provided by Yii. (opens new window). See also Yii Classes below. The base class has lots of methods.

Beginning on line 3, following ActiveRecord, are mDIS Model classes, provided by code-generation and by customization.

Class app\models\base\Base is hand-coded, the others are code-generated. They can be customized (by editing), but this is optional.

Actually, for consistency reasons app\models\base\Base should be called app\models\base\BaseRecord (just as mDIS Importers and Reports), but the class hierarchy is already pretty deep and names would get unreasonably long, so we prefer the shorter class names.

mDISModel classes

The Bigger Picture

An mDIS data model object represents one record in the corresponding data table. Every access to the record in the mDIS data tables is done via the model object. This way, it can be assured that validators are checked, behaviors are executed, etc.

These classes are hand-coded, as well as automatically created by the code-generator of the mDIS Templates-Manager.

Class Base

Abstract base class for all mDIS data models. Hand-coded.

base.png

The behaviors() method should be modified if a new AttributeBehavior is added system-wide.

Class BaseCoreCore

For every MySQL data table containing science data, a base class (e.g. base/BaseCoreCore.php) is generated by the Templates-Manager. This file should not be modified, since it could be overridden by the Templates-Manager.

It implements:

  • textfield default values (in the DEFAULT_VALUES constant array and in the init() method)
  • textfield datatypes and validators (in the rules() method)
  • textfield labels, column-labels (in the attributeLabels() method),
  • calculations (in lowermost method beforeSave()).
  • lots of getXXX() methods that return \yii\db\ActiveQuery (opens new window) objects TBC

base-core-core

The rules() method might have some additional value, 'safe'. This means this attribute is not constrained by some validation rules and still marked as 'safe'. We can assign anything to this field. If was not marked as 'safe', it would be ignored by the setAttributes() method of the yii\base\Model class. That feature allows us to pass the entire content of $_POST to setAttributes() and be sure that only expected values will be assigned to the model. (See also "'Unsafe' attributes and Scenarios" below)

The GENERATED constant contains a Unix timestamp.

Class CoreCore

Additionally, the Template-Manager has created a mostly empty class (e.g. CoreCore.php) that extends the BaseCoreCore class above. If you want to modify the data model manually, you should do it here:

core-core

You can add custom behaviours in the behaviors() method for instance.

The fields() method modifies the capabilities of the Filter Bar.

You can add your own rules() method here. (Take a look at ImageOfTheDay.php, ListValues.php, ListValuesSearch.php, MessageOfTheDay.php, Post.php, Widgets.php for inspiration.)

Class CoresForm

This class is not a Model Class but for completeness we list it here, because this class inherits from CoreCore. Class CoresForm has only 1 method: scenarios(), which returns a list of scenarios and the corresponding active attributes.

The CoresForm::scenarios() method overrides a base-class method 6 levels up, the yii\db\Model::scenarios() method.

Scenarios

What are Scenarios?

They are Yii built-in features. Scenarios allow you to define different sets of attributes that are active in different conditions.

Form Scenarios: Create, Edit or View.

??? (A validation scenario represents a search-, sort- or filter-action requested by the user where the mDIS model objects must validate sets of input parameters or criteria). TBC

Test case Scenarios

cores-form

It belongs to a different namespace, app\forms so the full name is app\forms\CoresForm\. The file is located in directory backend/forms (not backend/models).

Unsafe attributes and Scenarios

From the PHPdoc of the yii\db\Model::scenarios() class:

By default, an active attribute is considered "safe" and can be "massively assigned". If an attribute should NOT be massively assigned (thus considered unsafe), please prefix the attribute with an exclamation character (e.g. '!rank'). (In 2019, we do not use this construct in mDIS PHP code).

The default implementation of this method will return all scenarios found in the rules() declaration. A special scenario named SCENARIO_DEFAULT will contain all attributes found in the rules(). Each scenario will be associated with the attributes that are being validated by the validation rules that apply to the scenario.

The returned array should be in the following format:

[
   'scenario1' => ['attribute11', 'attribute12', ...],
   'scenario2' => ['attribute21', 'attribute22', ...],
   ...
]
1
2
3
4
5

Search Classes

These are the PHP classes implementing the Hierarchical Filter Bar and the "Filter by Values" search feature of the mDIS data entry forms.

Together with the data model, two search classes have been automatically generated, (1) BaseCoreCoreSearch.php) with some boilerplate code, and (2) CoreCoreSearch.php which is mostly empty (on purpose). These classes are used to find records based on query values entered by the mDIS user.

Class BaseCoreCoreSearch

base-core-core-search.png

The BaseCoreCoreSearch class contains a line of generated code for each table attribute. The other code that is needed is highly redundant. Therefore it has been factored out from each BaseXXXSearch class to helper class\app\models\SearchModelTrait, see below.

Class CoreCoreSearch

This class is empty on purpose. Modifications are almost never needed at this time (2019).

core-core-search.png

Class CoresFormSearch

This class is empty on purpose. Modifications are almost never needed at this time (2019).

cores-form-search.png

Helper Classes

Class SearchModelTrait

vscode--searchmodeltrait

The BaseXXXSearch* classes contain boilerplate code for each table attribute. The classes use a hand-coded trait-class SearchModelTrait.php that contains methods that would otherwise identical in every base search class.

If you do not like this code and you need to modify how records of a data model can be searched for, you should override some of the methods in the derived search model class (i.e. CoreCoreSearch.php), which is empty on purpose and can be customized.

(Developer page)

Yii Classes

For completeness, we show the Yii base classes from which mDIS class app\models\base\Base inherits.

Note that our app\model\Base class (in row 7) "stems from" a hierarchy of 5 base classes above it (beginning in row 2).

# Yii Classes
yii\BaseObject
  yii\Component
    yii\db\Model
      yii\dbBaseActiveRecord           # implements ActiveRecordInterface
        yii\db\ActiveRecord            # parent class of mDIS 'Base.php' class
          app\models\base\Base         # mDIS class
1
2
3
4
5
6
7

These are the constants and methods that the Yii2 framework provides, from top to bottom:

yii\base\BaseObject class

BaseObject is the base class that implements the property feature.

class BaseObject implements Configurable. Methods from this interface are not shown here separately.

BaseObject

yii\base\Component class

Component is the base class that implements the property, event and behavior features.

Component

yii\db\Model class

yii\db\Model is the base class for data models.

yii\db\Model implements the following commonly used features:

  • attribute declaration: by default, every public class member is considered as a model attribute
  • attribute labels: each attribute may be associated with a label for display purpose
  • "massive" attribute assignment with setAttributes($_POST)
  • scenario-based validation

class Model extends Component implements StaticInstanceInterface, IteratorAggregate, ArrayAccess, Arrayable. The respective methods from these interfaces are not shown here separately.

Model (1) Model (2)

yii\db\BaseActiveRecord class

db\BaseActiveRecord is the base class for classes representing relational data in terms of objects.

See \yii\db\ActiveRecord for a concrete implementation. Official Documentation (opens new window)

dbBaseActiveRecord (1) dbBaseActiveRecord (2) dbBaseActiveRecord (3)

Implements the save() method which, if successful, magically sets a new CoreCore->id field value as a side-effect. (? is this important?)

yii\db\ActiveRecord class

ActiveRecord is the concrete implementation of its abstract superclass db\BaseActiveRecord, and represents relational data in terms of objects.

Official Documentation (opens new window)

activerecord

app\models\base\Base class

The first mDIS class, located at level 6 in the inheritance hierarchy. (See above.)

base.png

mDIS adds another 3 hierarchy levels, making it 9 or 10 levels in total. It depends on how you count them.