1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. This forum has been archived. New threads and replies may not be made. All add-ons/resources that are active should be migrated to the Resource Manager. See this thread for more information.

Installer

Discussion in 'Development Tutorials [Archive]' started by Robbo, Jul 6, 2011.

  1. Robbo

    Robbo Well-Known Member

    This is the start of an abstract class to make creating installers easier for developers. What I am about to post hasn't even been tested yet but it gives you an idea of where I am going with it, I would like for developers to add their input (and changes when I add to git) to make our lives easier.

    PHP:
    <?php

    abstract class XenTrader_Installer_Abstract
    {
        protected 
    $_db;
        protected 
    $_existingAddon;
        protected 
    $_addonData;
        protected 
    $_rebuildContentCache false;

        public function 
    __construct($existingAddon$addonData)
        {
            
    $this->_db XenForo_Application::get('db');
            
    $this->_existingAddon $existingAddon;
            
    $this->_addonData $addonData;
        }

        public function 
    __destruct()
        {
            if (
    $this->_rebuildContentCache)
            {
                
    XenForo_Model::create('XenForo_Model_ContentType')->rebuildContentTypeCache();
            }
        }

        public static function 
    intall($existingAddon$addonData)
        {
            
    $installer = new self($existingAddon$addonData);
            return 
    $installer->runInstall();
        }

        public static function 
    uninstall($existingAddon$addonData)
        {
            
    $installer = new self($existingAddon$addonData);
            return 
    $installer->runUninstall();
        }

        public function 
    runInstall()
        {
            
    $start 1;

            if (
    $this->_existingAddon)
                
    $start += $this->_existingAddon['version_id'];

            for (
    $v $start$v <= $this->_addonData['version_id']; ++$v)
                
    $this->_callVersionMethod($v);

            return 
    true;
        }

        public function 
    runUninstall()
        {
            for (
    $v $this->_existingAddon['version_id']; $v >= 0; --$v)
                
    $this->_callVersionMethod($vtrue);

            return 
    true;
        }
     
        protected function 
    _callVersionMethod($version$uninstall false)
        {
            if (
    method_exists($this'_' . ($uninstall 'un' '') . 'installVersion' $version))
                
    $this->{'_' . ($uninstall 'un' '') . 'installVersion' $version}();
        }

        protected function 
    _bulkAddContentType(array $types)
        {
            if (!
    is_array($types))
                return;

            foreach (
    $types as $type => $pairs)
            {
                if (!
    is_array($pairs))
                    continue;

                foreach (
    $pairs as $name => $value)
                    
    $this->_addContentType($type$name$value);
            }
        }

        protected function 
    _addContentType($type$name$value)
        {
            
    $this->_rebuildContentCache true;

            if (!
    $this->_db->fetchRow('SELECT * FROM xf_content_type_field WHERE content_type = ? AND field_name = ?', array($type$name)))
                
    $this->_db->insert('xf_content_type_field', array(
                    
    'content_type' => $type,
                    
    'field_name' => $name,
                    
    'field_value' => $value)
                );

            if (!
    $this->_db->fetchRow('SELECT * FROM xf_content_type WHERE content_type = ?'$type))
                
    $this->db->insert('xf_content_type', array('content_type' => $type'addon_id' => $this->_addonData['addon_id'], 'fields' => ''));
        }

        protected function 
    _removeContentType($type$name null)
        {
            
    $this->_rebuildContentCache true;

            
    $handlers = array(
                
    'alert_handler_class' => 'xf_user_alert',
                
    'news_feed_handler_class' => 'xf_news_feed',
                
    'report_handler_class' => array('xf_report''_removeReportComments'),
                
    // TODO: add the rest of the possible handlers
            
    );

            
    $single false;
            if (
    $name && isset($handlers[$name]))
                
    $single $handlers[$name];
            else
                
    $name '*';

            
    $this->_db->delete('xf_content_type', array('content_type = ?' => $type));
            
    $this->_db->delete('xf_content_type_field', array('content_type = ? AND field_name = ?' => array($type$name)));

            if (
    $single)
            {
                if (
    is_array($single))
                {
                    if (
    method_exists($this$single[1]))
                        
    $this->$single[1]($type);

                    
    $single $single[0];
                }

                
    $this->_db->delete($single, array('content_type = ?' => $type));
                return;
            }

            foreach (
    $handlers as $handle)
            {
                if (
    is_array($handle))
                {
                    if (
    method_exists($this$handle[1]))
                        
    $this->$handle[1]($type);

                    
    $handle $handle[0];
                }

                
    $this->_db->delete($handle, array('content_type = ?' => $type));
            }
        }

        protected function 
    _removeReportComments($type)
        {
            
    $reportIds $this->_db->fetchCol('SELECT report_id FROM xf_report WHERE content_type = ?'$type);
            if (!empty(
    $reportIds))
                
    $this->_db->delete('xf_report_comment', array('report_id IN (' implode(','$reportIds) . ')'));
        }

        protected function 
    _addTableColumn($table$field$info$after '')
        {
            
    $columns $this->_db->describeTable($table);

            if (isset(
    $columns[$field]))
                return;

            if (isset(
    $columns[$after]))
                
    $info .= ' AFTER ' $after;

            
    $this->_db->query("ALTER TABLE '$table' ADD '$field$info");
        }

        protected function 
    _removeTableColumn($table$field)
        {
            
    $columns $this->_db->describeTable($table);

            if (isset(
    $columns[$field]))
            {
                
    $this->_db->query("ALERT TABLE '$table' DROP '$field'");
            }
        }
    }
    It handles content types nicely (haven't added everything there yet) including cleaning up old data when you remove one. It also has add and remove table column methods which check if columns exist for you.

    You extend this class and create a method for each version id. _installVersion1, _installVersion2, _uninstallVersion1 etc. If you ignore the abstract class it is overall a nice OO class to work with (no staticness).
     
    Hoffi likes this.
  2. Robbo

    Robbo Well-Known Member

    Here is a before and after of my install script for XenTrader.

    PHP:
    <?php

    [B]Before[/B]
    class 
    XenTrader_Installer
    {
        public static function 
    install()
        {
            
    self::_createTables();
            
    self::_addContentTypeHandler('alert_handler_class''XenTrader_AlertHandler_Feedback');
            
    self::_addContentTypeHandler('news_feed_handler_class''XenTrader_NewsFeedHandler_Feedback');
            
    self::_addContentTypeHandler('report_handler_class''XenTrader_ReportHandler_Feedback');

            return 
    true;
        }

        public static function 
    uninstall()
        {
            
    $db XenForo_Application::get('db');

            
    XenForo_Db::beginTransaction();

            
    $db->query('DROP TABLE `xentrader_feedback`');
            
    $db->query('DROP TABLE `xentrader_user`');

            
    $db->delete('xf_user_alert', array("content_type = 'feedback'"));
            
    $db->delete('xf_news_feed', array("content_type = 'feedback'"));
            
    $reportIds $db->fetchCol("SELECT report_id FROM xf_report WHERE content_type = 'feedback'");
            if (!empty(
    $reportIds))
                
    $db->delete('xf_report_comment', array('report_id IN (' implode(','$reportIds) . ')'));
            
    $db->delete('xf_report', array("content_type = 'feedback'"));

            
    $db->delete('xf_content_type_field',  array("content_type = 'feedback'"));

            
    XenForo_Db::commit();

            return 
    true;
        }

        protected static function 
    _createTables()
        {
            
    $db XenForo_Application::get('db');

            
    $db->query('
                CREATE TABLE IF NOT EXISTS `xentrader_feedback` (
                      `feedback_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                      `from_user_id` int(10) unsigned NOT NULL,
                      `to_user_id` int(10) unsigned NOT NULL,
                      `from_username` varchar(50) NOT NULL,
                      `to_username` varchar(50) NOT NULL,
                      `thread_id` int(10) NOT NULL,
                      `thread_title` varchar(150) NOT NULL,
                      `feedback` varchar(80) NOT NULL,
                      `rating` tinyint(1) NOT NULL,
                      `type` enum(\'buy\',\'sell\',\'trade\') NOT NULL,
                      `feedback_date` int(10) unsigned NOT NULL,
                      PRIMARY KEY (`feedback_id`)
                ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;'
    );

            
    $db->query('
                CREATE TABLE IF NOT EXISTS `xentrader_user` (
                    `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                    `positive` smallint(10) unsigned NOT NULL,
                    `neutral` smallint(10) unsigned NOT NULL,
                    `negative` smallint(10) unsigned NOT NULL,
                    `total` smallint(10) NOT NULL,
                    `rating` tinyint(3) unsigned NOT NULL,oh,
                    PRIMARY KEY (`user_id`)
                ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;'
    );

        }

        protected static function 
    _addContentTypeHandler($name$value)
        {
            
    $db XenForo_Application::get('db');

            if (!
    $db->fetchOne("SELECT * FROM xf_content_type_field WHERE content_type = 'feedback' AND field_name = '{$name}'"))
                
    $db->insert('xf_content_type_field', array(
                    
    'content_type' => 'feedback',
                    
    'field_name' => $name,
                    
    'field_value' => $value)
                );
        }
    }
    After
    PHP:
    <?php
     
     
    class XenTrader_Installer_Core extends XenTrader_Installer_Abstract
     
    {
         protected function 
    _installVersion1()
         {
             
    $this->_db->query('
                 CREATE TABLE IF NOT EXISTS xentrader_feedback (
                       `feedback_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                       `from_user_id` int(10) unsigned NOT NULL,
                       `to_user_id` int(10) unsigned NOT NULL,
                       `from_username` varchar(50) NOT NULL,
                       `to_username` varchar(50) NOT NULL,
                       `thread_id` int(10) NOT NULL,
                       `thread_title` varchar(150) NOT NULL,
                       `feedback` varchar(80) NOT NULL,
                       `rating` tinyint(1) NOT NULL,
                       `type` enum(\'buy\',\'sell\',\'trade\') NOT NULL,
                       `feedback_date` int(10) unsigned NOT NULL,
                       PRIMARY KEY (`feedback_id`)
                 ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci'
    );
     
             
    $this->_db->query('
                 CREATE TABLE IF NOT EXISTS xentrader_user (
                     `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                     `positive` smallint(10) unsigned NOT NULL,
                     `neutral` smallint(10) unsigned NOT NULL,
                     `negative` smallint(10) unsigned NOT NULL,
                     `total` smallint(10) NOT NULL,
                     `rating` tinyint(3) unsigned NOT NULL,
                     PRIMARY KEY (`user_id`)
                 ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci'
    );
     
             
    $this->_bulkAddContentType(array(
                 
    'feedback' => array(
                     
    'alert_handler_class' => 'XenTrader_AlertHandler_Feedback',
                     
    'news_feed_handler_class' => 'XenTrader_NewsFeedHandler_Feedback',
                     
    'report_handler_class''XenTrader_ReportHandler_Feedback'
                 
    )
             ));
         }
     
         protected function 
    _installVersion4()
         {
             if (isset(
    $this->_existingAddon['version_id']))
             {
                 
    $this->_db->query('ALTER TABLE xentrader_feedback CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci');
                 
    $this->_db->query('ALTER TABLE xentrader_user CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci');
             }
         }
     
         protected function 
    _uninstallVersion1()
         {
             
    XenForo_Db::beginTransaction();
     
             
    $this->_db->query('DROP TABLE `xentrader_feedback`');
             
    $this->_db->query('DROP TABLE `xentrader_user`');
     
             
    $this->_removeContentType('feedback');
     
             
    XenForo_Db::commit();
         }
     }
    I have been attempted to get more developer interest in tools / libraries etc to make developing mods for XenForo a faster process. This is the first thing I have had time for to start pushing for that to happen.
     

Share This Page