Application Modules - Structure

Each module represents a separate physical directory, where placed it's all files. Some of the files may be moved to another folders according to develop needs while installation/unstallation process. All modules must be placed in protected/modules/ directory.

Below the minimum file structure for the standard module. Take in account that you may define update files like: update.002.mysql.sql, update.003.mysql.sql, etc. While updating procedure the system will run them automatically according to their order.

Here the file structure for a real News module:


info.xml - change log file
Version 0.0.1
----------------------------
Initial release    


info.xml - main information file
<?xml version="1.0" encoding="utf-8"?>
<install version="1.0" type="module">
    <name>News</name>
    <description>News module allows creating and displaying news on the site</description>				 
    <version>0.0.1</version>
    <creationDate>15/08/2013</creationDate>
    <lastChangedDate>29/09/2013</lastChangedDate>
    <author>ApPHP</author>
    <authorEmail>info@apphp.com</authorEmail>
    <authorUrl>http://www.apphp.com</authorUrl>
    <copyright>ApPHP</copyright>
    <license>LGPL</license>
    <manual></manual>
    <icon>assets/modules/news/images/icon.png</icon>
    <moduleType>application</moduleType>
    <code>news</code>
    <files>
        <filename>info.xml</filename>
        <components>
            <filename>NewsComponent.php</filename>
        </components>
        <config installationPath="protected/config/">
            <filename exclude="yes">main.php</filename>
            <filename>news.php</filename>
        </config>
        <controllers>
            <filename>NewsController.php</filename>
        </controllers>
        <data>
            <install>install.mysql.sql</install>
            <update>
                <filename>update.002.mysql.sql</filename>
                <filename>update.003.mysql.sql</filename>
            </update>
            <uninstall>uninstall.mysql.sql</uninstall>
        </data>
        <images installationPath="assets/modules/news/images/">
            <filename>icon.png</filename>
        </images>
        <robots exclude="yes">
            <filename>robots.txt</filename>
        </robots>
        <messages installationPath="protected/messages/*">
           	<filename>news.php</filename>
        </messages>
    </files>
</install>

config/main.php - main configuration file
<?php
return array(
    // Module classes
    'classes' => array(
        'NewsComponent',
        'News',
    ),
    // Management links
    'managementLinks' => array(
        A::t('news', 'News') => 'news/manage'
    ),    
);    

config/news.php - special configuration (movable file, will be copied to protected/config/ directory)
<?php
return array(
    // Module components (optional)
    'components' => array(
        'NewsComponent' => array('enable' => true, 'class' => 'NewsComponent'),               
    ),

    // URL manager (optional)
    'urlManager' => array(
        'rules' => array(
            'news/view/id/([0-9]+)' => 'news/view/id/{$0}',
            'news/view/id/([0-9]+)/(.*?)' => 'news/view/id/{$0}',
            'news/view/([0-9]+)' => 'news/view/id/{$0}',
            'news/view/([0-9]+)/(.*?)' => 'news/view/id/{$0}',
            'news/([0-9]+)' => 'news/view/id/{$0}',
            'news/([0-9]+)/(.*?)' => 'news/view/id/{$0}',
        ),
    ),    

    // Default Backend url (optional, if defined - will be used as application default settings)
    'backendDefaultUrl' => 'news/manage',

    // Default settings (optional, if defined - will be used as application default settings)
    'defaultErrorController' => 'NewsError',
    'defaultController' => 'news',
    'defaultAction' => 'view',
);    

components/NewsComponent.php - module main component
<?php
class NewsComponent extends CComponent
{
    const NL = "\n";

    /**
     * Draws last news side block
     */
    public static function drawNewsBlock($title = '')
    {
        $output = '';
        $headerLength = 80;
        $newsCount = 3;

        $headerLength = ModulesSettings::model()->param('news', 'news_header_length');
        $newsCount = ModulesSettings::model()->param('news', 'news_count');
        
        // Fetch datetime format from settings table
        // Or $settings = Bootstrap::init()->getSettings();
        $settings = Settings::model()->findByPk(1);
        $dateTimeFormat = $settings->date_format;
        
        $news = News::model()->findAll(
            array(
                'condition'=>'is_published = :isPublished AND created_at < :currentDate',
                'order'=>'created_at DESC', 'limit'=>'0, '.(int)$newsCount
            ),
            array(':isPublished'=>1, ':currentDate'=>LocalTime::currentDateTime())
        );

        $output .= '<div class="side-panel-block">';
        $output .= '<h3 class="title">'.$title.'</h3>';
        if(is_array($news) && count($news) > 0){
            foreach($news as $key => $val){
                $output .= '<div class="block-body">	
                    <p class="block-title">
                        <a href="news/view/id/'.$val['id'].'">
                            '.CString::substr($val['news_header'], $headerLength).'
                        </a>
                    </p>
                    <p class="block-date">'.date($dateTimeFormat, strtotime($val['created_at'])).'</p>
                    <p class="block-content">'.CString::substr(strip_tags($val['news_text']), 125, '', true).'
                        <a class="more more-news" href="news/view/id/'.$val['id'].'">
                            '.A::t('news', 'read more').' »
                        </a>
                    </p>
                </div>';							
            }
        }else{
            $output .= A::t('news', 'No News Found');
        }
        $output .= '</div>';
        
        return $output;
    }    
}