Expert's model generation with Gii

Efficient coding with Yii implies efficient use of the code generator. This article shares the personalised Gii templates that were built starting from the standard templates proposed by AweGen.

This wiki shares and discusses the generated model for the 'authitemchild' table used by the RBAM scheme.

Generating a model consists of two files: one file that is generated in the 'model/_base' directory, and another file that is generated in the 'model' directory.

The file located in the 'model/_base' directory should NEVER be edited manually. One goal is that it can be generated again after table changes. The file located in the 'model' directory should NEVER be generated again. It should contain your custom code.

The use of the generated files requires the availability of the ERememberSearchFilters extension and the RelatedSearchBehavior extension.

I discuss the reasons for the code below inside the code extraction with "EXPLICATION:"

_models_base\BaseAuthitemchild.php_

<?php
/********************************************************************/
/* @copyright Copyright (c) 2012-2014, YOUR COPYRIGHT NAME          */
/********************************************************************/
/**
 * This is the model base class for the table "authitemchild".
 *
 * Columns in table "authitemchild" available as properties of the model:
 *  @property string $parent
 *  @property string $child
 *
 * Relations of table "authitemchild" available as properties of the model:
 *  @property Authitem $child0
 *  @property Authitem $parent0
 *

EXPLICATION: RelatedSearchBehavior provides 'autoscopes' which provides scopes
             to search fields automatically.  This comment generation tells the user
             and most of all the IDE which methods are available.
             The autoscopes are virtual and can not be detected otherwise by the IDE.

 * Columns in table "authitemchild" are available as scopes of the model (with RelatedSearchBehavior):
 *  @method Authitemchild parent() parent(mixed $value, boolean $partialMatch=false, string $operator='AND', boolean $escape=true)
 *  @method Authitemchild child() child(mixed $value, boolean $partialMatch=false, string $operator='AND', boolean $escape=true)
 * Documentation for default methods to provide the class specific method signature.
 *  @method Authitemchild resetScope() resetScope(boolean $resetDefault=true)
 */

EXPLICATION: The base class is an abstract class which can be regenerated again later.

abstract class BaseAuthitemchild extends CActiveRecord {

EXPLICATION: Referring to the columns through constants avoid errors - PHP will complain
             about misspelled constants, not about misspelled strings.
             In case you have to change the name of the column in the future, this will
             also help you detect where else you have to change your code.

    /* Constants to reference table columns */
    const PARENT = 'parent'; // Parent
    const CHILD = 'child'; // Child

    public static function model($className = __CLASS__) {
        return parent::model($className);
    }

    public function tableName() {
        return 'authitemchild';
    }

    public function rules() {
        return array(
            array('parent, child', 'required'),
            array('parent, child', 'length', 'max' => 64),
            array('parent, child', 'safe', 'on' => 'search'),
        );
    }

EXPLICATION: The generator makes a guess at the best textual representation for the model.
             This can be changed in the actual Model class.
             It is helpfull for debugging (you can "print" the class directly).

    public function __toString() {
        return (string) $this->parent;
    }

    /**
     * Updates records with the specified condition.
     *
     * Modifies the standard method in that it takes into account the model conditions to select the records to update.
     *
     * @see CActiveRecord::updateAll()
     */

EXPLICATION: The default updateAll() does not support scopes, this is a modified version
             which supports scopes.

    public function updateAll($attributes,$condition=array(),$params=array())
    {
        //Next line must be added before defining the criteria/scopes.
        //Authitemchild::model()->getDbCriteria()->alias=Authitemchild::model()->tableName();
        $crit=new CDbCriteria($condition);
        $this->applyScopes($crit);
        return parent::updateAll($attributes,$crit);
    }

    /**
     * Deletes records with the specified condition.
     *
     * Modifies the standard method in that it takes into account the model conditions to select the records to update.
     *
     * @see CActiveRecord::updateAll()
     */

EXPLICATION: The default deleteAll() does not support scopes, this is a modified version
             which supports scopes.

    public function deleteAll($condition=array(),$params=array())
    {
        //Next line must be added before defining the criteria/scopes.
        //Authitemchild::model()->getDbCriteria()->alias=Authitemchild::model()->tableName();
        $crit=new CDbCriteria($condition);
        $this->applyScopes($crit);
        return parent::deleteAll($crit);
    }

    /**
     * Unset attributes of model when this is a search scenario
     * to avoid unwanted side effects of initial values set
     * in the database.
     *
     * @see CActiveRecord::afterConstruct()
     */

EXPLICATION: Any model is initialised with default values as defined in the database
             schema.  These defaults become search conditions when used as a filter
             for a CGridView for instance.
             This initializing code circumvents that function for searches.

    protected function afterConstruct() {
        if($this->getScenario()==='search') {
            $this->unsetAttributes();
        }
        parent::afterConstruct();
    }

    public function behaviors() {
        return array(

EXPLICATION: Set up the remember filters behavior because I like this feature.

            'ERememberFiltersBehavior' => array(
                'class' => 'ext.ERememberFiltersBehavior.ERememberFiltersBehavior',
                'defaults'=>array(),
                'defaultStickOnClear'=>false,
            ), 

EXPLICATION: Setup the RelatedSearchBehavior extension.
             Provide some sample configuration as a reminder.
             The configuration has to be done in the actual model class
                 (not in the base class).

            'relatedsearch'=>array(
                    'class'=>'RelatedSearchBehavior', 
                    'relations'=>array( 
                            /* Examples: 
                            'owner'=>'ownerUser.displayname', 
                            'serial'=>array('field'=>'device.device_identifier','searchvalue'=>'serialSearch'),
                            */ 
                            /* Fields for easy access */ 
                    ), 
            ), 
);
    }

    public function relations() {
        return array(
            'child0' => array(self::BELONGS_TO, 'Authitem', 'child'),
            'parent0' => array(self::BELONGS_TO, 'Authitem', 'parent'),
        );
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels()
    {
        return array(

EXPLICATION:  All strings in my application are I18N.

            'parent' => Yii::t('app','lb.authitemchild.parent'), // Parent
            'child' => Yii::t('app','lb.authitemchild.child'), // Child
        );
    }

    /*
     * @return KeenActiveDataProvider
     */
    public function search() {

EXPLICATION: To be compatible across database types and alias configurations,
             the alias is determined using the provided method in Yii
               (it is not supposed to be 't' as in many examples on forums and
                in tutorials).

        $criteria = new CDbCriteria;
        $t=$this->getTableAlias(false,false);
        $ds=$this->getDbConnection()->getSchema();

EXPLICATION: Columns are quoted.  The code here does not use the column constants
             defined earlier basically because I did not get around to coding it
             yet, and also because this file is generated and not subject to
             human error.
             Generating the code referring to the above constants would be usefull
             to improve copy/paste behavior by the developer.

        $criteria->compare($ds->quoteColumnName("$t.parent"), $this->parent);
        $criteria->compare($ds->quoteColumnName("$t.child"), $this->child);


EXPLICATION: Calls relatedSearch() provided by the RelatedSearchBehavior extension.
             The sort configuration is commented and provided as a reminder.
             The original method is also provided as a reminder.
             Any changes to search() should be done in the actual Model class.
  
        return $this->relatedSearch(
            $criteria
            //,array('sort'=>array('defaultOrder'=>$this->getTableAlias(true,false).'.entity_id DESC'))
        );
        /*
        return new CActiveDataProvider(get_class($this), array(
                    'criteria' => $criteria,
                ));
        */
    }

}

models\Authitemchild.php

<?php
/********************************************************************/
/* @copyright Copyright (c) 2012-2014, YOUR COPYRIGHT NAME          */
/********************************************************************/

EXPLICATION: Rather than defining the path in the configuration,
             the base model to import is explicitally defined here.
             This is likely more efficient.

Yii::import('application.models._base.BaseAuthitemchild');
/**
 * Authitemchild: model class for the table authitemchild
 *
 * The following are available application defined scopes:
 *   No scopes.
 *
 */

EXPLICATION: The actual model extends the base class (that can be regenerated).

class Authitemchild extends BaseAuthitemchild {
    /**
     * @param string $className
     * @return Authitemchild
     */
    public static function model($className = __CLASS__) {
        return parent::model($className);
    }

    /**
     * (non-PHPdoc)
     * @see CActiveRecord::init()
     */
    public function init() {
        return parent::init();
    }

    /**
     * (non-PHPdoc)
     * @see BaseAuthitemchild::behaviors()
     */
     public function behaviors() {
        $behaviors=parent::behaviors();

EXPLICATION: The base set of behaviors is set in the base model, they are
             configured/extended through array assignments.
             Note that the code from the base model is not copied as it may
             be regenerated.
             The code below is commented and provided as a reminder of the syntax.
 
        //$behaviors['relatedsearch']['relations']['entity_displayname']='entity.displayname';
        //$behaviors['relatedsearch']['relations']['entity_displayname']=array('field'=>'relation.field','searchvalue'=>'localVar','value'=>'relation.field');
        return $behaviors;
    }

    /**
     * Add automatic scopes for attributes (uses RelatedSearchBehavior).
     */

EXPLICATION: This code is necessary for the autoscopes provided by RelatedSearchBehavior.

    public function __call($name,$parameters) {
        try {
            return parent::__call($name,$parameters);
        } catch (CException $e) {
            if(preg_match(
                    '/'.Yii::t(
                            'yii',
                            quotemeta(
                                    Yii::t(
                                            'yii',
                                            '{class} and its behaviors do not have a method or closure named "{name}".'
                                    )
                            ),
                            array('{class}'=>'.*','{name}'=>'.*')
                    )
                    .'/',$e->getMessage())) {
                return $this->autoScope($name, $parameters);
            } else {
                throw $e;
            }
        }
    }

    /**
     * (non-PHPdoc)
     * @see CActiveRecord::scopes()
     */

EXPLICATION: This code is generated as a starting point for adding scopes with a
             reminder of how to define the references to the columns.
             In this sample code, it is suggested to use the constant generated
             in the BaseModel.

    public function scopes() {
        $table=$this->getTableAlias(false,false);
        $ds=$this->getDbConnection()->getSchema();
        //$ACOLUMN=$ds->quoteColumnName($table.'.'.self::ACOLUMN);
        return array(
        );
    }
}

The Gii code to use with AweGen is in a forum entry because a wiki does not allow attachments, and this is not really an extension.

1 0
2 followers
Viewed: 16 882 times
Version: 1.1
Category: Tips
Written by: le_top
Last updated by: le_top
Created on: Jul 18, 2014
Last updated: 9 years ago
Update Article

Revisions

View all history

Related Articles