Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

CodeSmith creates a code template for Yii Framework that generates ActiveRecord


May 25, 2021 CodeSmith


Table of contents


Create a code template for Yii Framework that builds ActiveRecord

In CodeSmith using tutorial (3): Autogenerate Yii Framework ActiveRecord We use SchemaExploer to generate a simple ActiveRecord class from the database for Yii Framework without taking into account the relationship between tables and tables. In this example, we used CodeSmith to create a generic code template for Yii Framework, using the SchemaExploer described in the example above, but in the example that came with CodeSmith, there is an example of generating Hibernate, which can be found in the CodeSmith Usage Tutorial (1): Overview, CodeSmith provides the source code for this template, and codeSmith.SchemaHelper (CodeSmith does not provide the appropriate documentation), but you can learn about the general usage by reading the CodeSmith template.

To build a relation between the Yii Framework ActiveRecord classes, first look at the relationship between tables and tables:

The relationship between the two AR classes is directly related through the relationship between the data tables represented by the AR class. F rom a database perspective, there are three relationships between Table A and B: one-to-many (one-to-many, for example, tbl_user and tbl_post), one-to-one (one-to-one for example, tbl_user and tbl_profile), and many-to-many (for example, tbl_category and tbl_post). In AR, there are four relationships:

  • BELONGS_TO (belongs to): If the relationship between Table A and B is one-to-many, Table B belongs to Table A (for example Post belongs to User);

  • HAS_MANY (multiple): If the relationship between Table A and B is one-to-many, A has more than one B (for example, User has more than One Post);

  • HAS_ONE (one): This is HAS_MANY exception to the HAS_MANY, A has up to one B (for example, User has up to one Profile);

  • MANY_MANY: This corresponds to a many-to-many relationship in the database. B ecause most DBMSs do not directly support many-to-many relationships, an association table is required to split the many-to-many relationships into one-to-many relationships. I n our sample data structure, tbl_post_category is used for this purpose. I n AR terminology, we can explain MANY_MANY of BELONGS_TO for HAS_MANY and the HAS_MANY of the ar. For example, Post belongs to multiple (belongs to many) Category, and Category has multiple (hass many) Post.

This example is also using the Chinook database to modify the Yii Framework Development Tutorial (27) Database-Associated Active Record sample. The relationships between the data sheets are as follows:

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

PLINQO-NH code location in CodeSmith:

The default directory is C:\Program Files (x86)\CodeSmith\v6.5\Samples\Templates\Frameworks\PLINQO-NH

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

The main classes defined by CodeSmith.SchemaHelper are:

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

Several major classes are

  • EntityManager manages all Entity (corresponding to the entire database)
  • Entity entity class (corresponding to a single table, view)
  • IAssoication relationships (defining the relationship between tables and tables)
  • Types of AssoicationType relationships (see table below)

Based on AssociationType, the relationships between databases, and several relationships supported by Yii AR, you can define the following table:

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

The entire template is also master-from-template, enumerateing each Enterprise in EnterpriseManager, and then calling sub-templates to generate AR classes for each table:

public void Generate()
{
   EntityManager entityManager = CreateEntityManager();
   foreach(IEntity entity in entityManager.Entities)
    {
        if (!(entity is CommandEntity)) {
            RenderEntity(entity);
        }
    }
}

...

private void RenderEntity(IEntity entity)
{

    string folder=@"../models/";
    EntityTemplate entityTemplate = this.Create<EntityTemplate>();
    entityTemplate.SourceEntity = entity;
    entityTemplate.RenderToFile(folder+entity.Name+".php", true);
}

Sub-templates generate relations functions for AR based on each Enterprise's Assoications (relationship properties).

<?php

class <%= SourceEntity.Name %> extends CActiveRecord
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return '<%= SourceEntity.GetSafeName() %>';
    }

    <%if (SourceEntity.Associations.Count>0){ %>
    public function relations()
    {
        return array(
         <% IEnumerable<IAssociation> associations = SourceEntity.Associations; %>
         <% foreach(IAssociation association in associations) { %>
         <% if(association.Entity.Name!=association.ForeignEntity.Name) {%>
            <% if (association.AssociationType == AssociationType.ManyToOne
                || association.AssociationType==AssociationType.ManyToZeroOrOne) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::BELONGS_TO,
            '<%= association.ForeignEntity.Name %>',
            <%=GetBelongToKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.OneToMany
                || association.AssociationType==AssociationType.ZeroOrOneToMany) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::HAS_MANY,
            '<%= association.ForeignEntity.Name %>',
            <%=GetKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.OneToOne
                || association.AssociationType==AssociationType.OneToZeroOrOne) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::HAS_ONE,
            '<%= association.ForeignEntity.Name %>',
            <%=GetKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.ManyToMany) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::MANY_MANY,
            '<%= association.IntermediaryAssociation.Entity.Name %>',
            <%=GetManyToManyKey(association) %>
            <% } %>
         <% } %>
     <% } %>
        );
    }
    <% } %>
}

?>

<script runat="template">

public string ToCameral(string name)
{
    return StringUtil.ToCamelCase(name);
 }

public string GetKey(IAssociation association)
{
    string retString=string.Empty;

    if(association.Properties.Count>1)
    {
        retString="array(";
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.ForeignProperty.GetSafeName()+"',";
        }
        retString+="),";
    }else{
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.ForeignProperty.GetSafeName()+"'),";
        }

    }
    return retString;
}

public string GetBelongToKey(IAssociation association)
{
    string retString=string.Empty;

    if(association.Properties.Count>1)
    {
        retString="array(";
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.Property.GetSafeName()+"',";
        }
        retString+="),";
    }else{
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.Property.GetSafeName()+"'),";
        }

    }
    return retString;
}

public string GetManyToManyKey(IAssociation association)
{

    string retString="'"+association.ForeignEntity.GetSafeName()+"(";

    foreach (AssociationProperty associationProperty in association.Properties)
    {
        retString+=associationProperty.ForeignProperty.GetSafeName()+",";
    }
    IAssociation intermidateAssociation=association.IntermediaryAssociation;
    if(intermidateAssociation!=null)
    {
           foreach (AssociationProperty associationProperty in intermidateAssociation.Properties)
        {
            retString+=associationProperty.ForeignProperty.GetSafeName()+",";
        }
    }

    retString=retString.Substring(0,retString.Length-1);
    retString+=")'),";
    return retString;
}
</script>

The generated output can then generate a corresponding AR class for the table of the database, such as the generated Track class

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

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

    public function relations()
    {
        return array(
            'album'=>array(self::BELONGS_TO,'Album','AlbumId'),
            'genre'=>array(self::BELONGS_TO,'Genre','GenreId'),
            'mediatype'=>array(self::BELONGS_TO,'Mediatype','MediaTypeId'),
            'invoicelines'=>array(self::HAS_MANY,'Invoiceline','TrackId'),
            'playlists'=>array(self::MANY_MANY,'Playlist','playlisttrack(TrackId,PlaylistId)'),
        );
    }
}

If you really don't understand this example also doesn't matter, you can use the template directly, as long as you set the data source, if the database table has a prefix, such as Wordpress table has wp_ you can set the table prefix (not required)

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

This example Download if you need to use the template in this example, copy the codesmith directory under project protected directly into your own project, configure the data source (or table prefix) for codesmith.csp, and generate code.

CodeSmith creates a code template for Yii Framework that generates ActiveRecord

This example Downloads