Writing an installer for your CakePHP application

Published on April 16, 2007 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,..

13 comments baked

  • Damian April 17, 2007 at 15:57

    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 April 17, 2007 at 19:15

    @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 April 17, 2007 at 22:16

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

  • brandon April 18, 2007 at 06:39

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

  • cakebaker April 18, 2007 at 19:15

    @majna: Good idea, thanks!

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

  • Digital Spaghetti April 19, 2007 at 11:42

    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 April 20, 2007 at 18:54

    @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 April 28, 2007 at 14:24

    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 April 28, 2007 at 17:45

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

  • CCDC July 30, 2008 at 16:03

    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 July 31, 2008 at 19:19

    @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 November 19, 2008 at 12:07

    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 November 19, 2008 at 20:24

    @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!

Bake a comment




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

© daniel hofstetter. Licensed under a Creative Commons License