Writing an installer for your CakePHP application

Published on and tagged with cakephp  controller

If you write an application which the user must install on his own server, you have to think about the installation process of your application. In this article I will describe the approach I have used in my application.

This approach is heavily inspired by the one used by WordPress. That means the user has to create the database and to define the database settings in app/config/database.php himself. When this is done, the user must run the installer, which then creates the tables and does some application-specific stuff like creating a default user.

The installer is a usual controller, which is automatically called when the user accesses the application for the first time. For this purpose I added a bit of logic to app/config/routes.php:

if (file_exists(TMP.'installed.txt')) {
    // the routes for when the application has been installed
} else {
    Router::connect('/:action', array('controller' => 'installer'));
}

So as long as there is no “installed.txt” file, the user gets the first page of the installer when he requests the url of the application. The consequence of this logic is that we have to create such a file at the end of the installation process (see below).

The installer controller itself is straight-forward, each action is one step in the installation process. You can find a minimal version of the installer below:

// app/controllers/
uses('model' . DS . 'connection_manager');

class InstallerController extends AppController {
    var $uses = array();
		
    function beforeFilter() {
        if (file_exists(TMP.'installed.txt')) {
            echo 'Application already installed. Remove app/config/installed.txt to reinstall the application';
            exit();
        }
    }
		
    function index() {
    }
		
    function database() {
        $db = ConnectionManager::getDataSource('default');

        if(!$db->isConnected()) {
            echo 'Could not connect to database. Please check the settings in app/config/database.php and try again';
            exit();
        }
			
        $this->__executeSQLScript($db, CONFIGS.'sql'.DS.'app.sql');
			
        $this->redirect('/installer/thanks');
    }
		
    function thanks() {
        file_put_contents(TMP.'installed.txt', date('Y-m-d, H:i:s'));
    }
		
    function __executeSQLScript($db, $fileName) {
        $statements = file_get_contents($fileName);
        $statements = explode(';', $statements);
			
        foreach ($statements as $statement) {
            if (trim($statement) != '') {
                $db->query($statement);
            }
        }
    }
}

It is a pragmatic solution with the drawback that it is not really reusable,..

21 comments baked

  • Damian

    That’s a pretty nice solution. How would you prevent someone from deleting the installed.txt and accidentally re-running the installation? Provide some sort of install prompt? Maybe write a some junk to installed.txt and save the md5sum in the db for comparison?

  • cakebaker

    @Damian: Hm, good question. A possible solution could be to have a command line installer so you don’t need that “installed.txt” file.

  • majna

    nate suggested:
    add route file with default route to /install/
    after install, replace route.php with app production route.php
    chmod before…

  • brandon

    Nice! Now, all Cake needs is a package manager :)

  • cakebaker

    @majna: Good idea, thanks!

    @brandon: Yeah, a package manager would be cool :)

  • Digital Spaghetti

    Yea, a package manager would be great. I think a good way of doing this would be to have the installer check if /app/config/database.php exisits. If it does, prompt the user to type in their database details and have the installer write it.

    As well as then executing the SQL, you could use something like the ConfComponent from the bakery to manage all main site settings.

  • cakebaker

    @Digital Spaghetti: Yes, that’s a possible approach. I was too lazy to implement it as I saw the WordPress guys simply force the users to make the settings directly in the database config file ;-)

  • charlie

    Maybe it would be better to have a not_installed.txt file, so once installed it gets deleted. Then no problems about people deleting it by accident.

  • cakebaker

    @charlie: That’s a good idea, thanks!

  • CCDC

    Per @Digital Spaghetti’s comments, I want to display a form that allows the user to enter in database connection info that is stored in database.php, but I am getting a “Missing Database Connection” error due to the cake framework checking the connection before executing my installer controller.

    Is there any way to allow the installer_controller to run without cake making it’s database connection attempts?

    Here is my routes.php:

    if (file_exists(TMP.'not_installed.txt')) {
        Router::connect('/:action', array('controller' => 'installer'));    
    } else {
          Router::connect('/', array('controller' => 'users', 'action' => 'login'));
    }

    Here’s the installer_controller.php:

    <?php
    // app/controllers/
    uses('model' . DS . 'connection_manager');
    
    class InstallerController extends AppController {
        var $name = 'Installer';
        var $uses = array();
        var $helpers = array('Html');
    
        function beforeFilter() {        
            if (!file_exists(TMP.'not_installed.txt')) {
                echo('Application is already installed. Create app/config/not_installed.txt to reinstall the application.');
                exit();
            }
        }
    
        function index() {
        }
    
        function database() {        
            $db = ConnectionManager::getDataSource('default');
    
            if(!$db->isConnected()) {
                
                echo 'Could not connect to database. Please check the settings in app/config/database.php and try again';
            }
        }
    
        function thanks() {
            if (unlink(TMP.'not_installed.txt')) {
                $this->set('message', 'Installation is complete');
            } else {
                $this->set('message', 'The final step of the installation failed because ' . TMP.'not_installed.txt' . ' could not be deleted.');
            }
        }
    
        private function __createTables() {
            echo "__createTables()<br>";
    
            $this->__executeSQLScript($db, CONFIGS.'sql'.DS.'app.sql');
    
            $this->redirect('/installer/thanks');
        }
        
        private function __executeSQLScript($db, $fileName) {
            echo "__executeSQLScript()<br>";
            
            $statements = file_get_contents($fileName);
            $statements = explode(';', $statements);
    
            foreach ($statements as $statement) {
                if (trim($statement) != '') {
                    $db->query($statement);
                }
            }
        }
    }
    ?>

    database_form.ctp:

    <div id="database_form">
    	<?=$form->create('database')?>
    	<?=$form->input('database')?>
    	<?=$form->input('username')?>
    	<?=$form->input('password')?>
    	<?=$form->input('host')?>
    	<?=$form->end()?>
    </div>
  • cakebaker

    @CCDC: Hm, unless you use some database functionality in your AppController or you are calling the “database” action in the InstallerController I don’t see why you should get a “missing database connection” error. Maybe you can be more specific on when you get the error?

  • Symen Timmermans

    The problem CCDC describes is a very valid one.
    I’m struggling with this one also.
    Perhaps the installer should run some php script outside of Cakephp to circumvent this problem…

  • cakebaker

    @Symen: Ah, now I see the problem CCDC described. The problem is that the FormHelper tries to access the database, hence the “missing database connection” error. If you use plain HTML the error should disappear, but then you probably will have the problem that you cannot write database.php…

    As I described in the article, I think you can expect a user will be able to modify database.php manually if you describe it in the installation instructions.

    Another option is, as you mention, to write an installation script. Either as a php script independent of cakephp, or as a cake shell script.

    Hope that helps!

  • scls19fr

    There is an other problem with this kind of installation script…

    Your are trying to execute a SQL script inside the php code… but your SQL script contains informations such as table prefix when you are building tables… but this kind of information could be modified by user… (it would be a very bad idea to have to change both app/config/database.php and the SQL script)

    So the solution could be to have a SQL script with variables instead of table prefix (and database name)

    Unfortunately I don’t know how to do this.

    I like CakePHP very much but I really think that a well suported installation method for application which are build with CakePHP should be supported

    Two kinds of installations should be supported

    – the first one, modifying manually app/config/database.php , executing a SQL script (with database name and table prefix as variable), and executing other PHP code for post install

    – a second one with a form that user could fill in.

  • cakebaker

    @scls19fr: Thanks for your comment!

    Yes, table prefixes are a problem when using plain SQL. However, I think in the meantime this problem has been solved with the introduction of the schema concept, which allows you to specify the tables with PHP (and I assume it takes the table prefix in account when it translates those settings to SQL).

    I don’t think there should be support for installation methods in the core framework because only a minority of applications would benefit from it. And so far no solution emerged from the community, which could mean nobody is interested in it, or that it is not that easy to write a generic solution…

  • scls19fr

    Let’s imagine you want to build a wiki, a little CMS, a blog based on the CakePHP framework…

    I’m personnaly very interrested in writing a wiki with a wikka like syntax)
    The IBM tutorial http://www.ibm.com/developerworks/edu/os-dw-os-php-wiki1.html
    Create an interactive production wiki using PHP seems to be a good starting point for this !

    if you don’t provide a convenient installation method the project wouldn’t be available for the mass users.

  • cakebaker

    @scls19fr: Yes, I fully agree with you that you need a good installation method if you write a product people have to install. And for such projects this is important. However, of all projects realized with CakePHP I think only a minority will fall into this product category, most are custom web applications. And so providing an installer infrastructure probably has a low (or no) priority for the core devs.

    Hence it is something which has to be realized by some community members with a need for such an installer infrastructure…

  • scls19fr

    I’m using wikis since several years.
    When you have a look at the code it’s often so messy !
    (I won’t say what wiki I’m using ;-) )
    Using a good framework such as CakePHP would be interresting because medium users could more easily change layout, add new functionnality, …

  • scls19fr

    I’m using wikis since several years.
    When you have a look at the code it’s often so messy !
    (I won’t say what wiki I’m using ;-) )
    Using a good framework such as CakePHP would be interresting because medium users could more easily change layout, add new features, …

    Moreover if an application based on the framework is well know it full urge many other developpers to have a look at the framework.

  • cakebaker

    @scls19fr: Your point about messy code is true, and not only for wiki software ;-)

    Using a framework can prevent this to some degree by offering you a certain structure, but in the end it is up to the developers whether the code ends in a mess… However, it definitely decreases the learning curve for users (developers) who already know the framework.

    And yes, a well-known application could be a good promoter of the framework itself.

    Anyway, I am looking forward to your wiki application :)

  • CakePHP Developer Links - PRONIQUE Software

    […] […]

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License