X16 X16 - 2 months ago 7
PHP Question

Propel 2 — Noob here: how to create model, tables, etc. WITHOUT command line interface

Just started to work with Propel 2.0 ORM. Every and all tutorials are telling to work with schemas next way:


  1. Create schema in XML/JSON/YAML/PHP file;

  2. Run
    $ propel model:build



The question is: how to create or re-create or update models and data without using command line but just inside the php scripts? It might be neccessary for creating CMS module installers or something like this.

Thanks.

X16 X16
Answer

The Answer: Commander Class

A «Reinvent-The-Wheel» approach but I did not found any other way to work with Propel 2 without CLI.

use Propel\Runtime\Propel;
use Propel\Generator\Command;

use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Application;

/**
 * Class Commander
 *
 * A script-based approach to run Propel commands without the CLI.
 *
 * Usage:
 *
 * ```
 * $cmd = new Commander('<propel_command>', '<command_arguments>');
 * $cmd->run();
 * ...
 * $cmd->addCommand('<propel_command>', '<command_arguments>');
 * $cmd->run();
 * ```
 *
 * In case of migration tasks you must call
 * ```
 * ...->preMigrate(array('<path_to_schema_files_dir1>', ..., '<path_to_schema_files_dirN>'), '<temp_dir>');
 * ```
 * to gather all schemas together and analyze 'em with propel:diff.
 *
 * Then after the diff and migrate are complete you must call ``postMigrate()`` to remove temporary
 * schema copies.
 *
 */
class Commander
{
    private $command,
            $parameters,
            $migrationTempSource;

    public function __construct($cmd = '', $params = '')
    {
        $this->addCommand($cmd, $params);
    }

    /**
     * Prepare schema files to be analyzed before the migration process.
     * @param array $schemaDirs Array of strings with schema directories
     * @param string $tmpSchemaDir Temporary directory to copy schemas to.
     * This path also must be used as a --schema-dir option value during the
     * diff and migrate tasks
     * @return boolean $result
     */
    public function preMigrate($schemaDirs = array(), $tmpSchemaDir)
    {
        $result = false;
        $filelist = [];
        foreach($schemaDirs as $path)
        {
            if(is_dir($path))
            {
                $f = $this->seekFiles($path);
                $filelist = count($f) > 0 ? array_merge($filelist, $f) : $f;
            }
        }
        if(!file_exists($tmpSchemaDir))
        {
            mkdir($tmpSchemaDir, 0777, true);
        }
        foreach($schemaDirs as $path)
        {
            if(is_dir($path))
            {
                $f = $this->seekFiles($path);
                foreach($f as $file)
                {
                    copy($path . '/' . $file, $tmpSchemaDir . '/' . $file);
                }
            }
        }
        $this->migrationTempSource = $tmpSchemaDir;
        return $result;
    }

    /**
     * Removes the temporary schema files after the diff and migrate tasks are complete.
     *
     * @param bool $removeTmpDir Set to true if you want to remove the whole temporary
     * directory, not just the schema files.
     * @return bool
     */
    public function postMigrate($removeTmpDir = false)
    {
        $result = false;
        $dir = scandir($this->migrationTempSource);
        foreach($dir as $d)
        {
            if($d != '.' && $d != '..')
            {
                unlink($this->migrationTempSource . '/' . $d);
            }
        }

        if($removeTmpDir === true)
        {
            @rmdir($this->migrationTempSource);
        }

        return $result;
    }

    private function seekFiles($dir)
    {
        $res = [];
        if(is_dir($dir))
        {
            $d = scandir($dir);
            foreach($d as $dd)
            {
                if($dd != '.' && $dd != '..')
                {
                    if((strpos($dd, 'schema.xml') == strlen($dd)-10) || ($dd == 'schema.xml'))
                    {
                        $res[] = $dd;
                    }
                }
            }
        }
        return $res;
    }

    public function addCommand($cmd = '', $params = '')
    {
        $this->command = $cmd;
        $this->parameters = explode(' --', $params);
    }

    public function run()
    {
        if($this->command == '') return false;

        $callCommandClass = '';
        $cmdParts = explode(':', $this->command);
        switch($cmdParts[0])
        {
            case 'config':
                switch($cmdParts[1])
                {
                    case 'convert':
                        $callCommandClass = 'ConfigConvertCommand';
                    break;
                }
            break;

            case 'diff':
                $callCommandClass = 'MigrationDiffCommand';
            break;

            case 'migration':
                switch($cmdParts[1])
                {
                    case 'create':
                        $callCommandClass = 'MigrationCreateCommand';
                    break;

                    case 'diff':
                        $callCommandClass = 'MigrationDiffCommand';
                    break;

                    case 'up':
                        $callCommandClass = 'MigrationUpCommand';
                    break;

                    case 'down':
                        $callCommandClass = 'MigrationDownCommand';
                    break;

                    case 'status':
                        $callCommandClass = 'MigrationStatusCommand';
                    break;

                    case 'migrate':
                        $callCommandClass = 'MigrationMigrateCommand';
                    break;
                }
            break;

            case 'model':
                switch($cmdParts[1])
                {
                    case 'build':
                        $callCommandClass = 'ModelBuildCommand';
                    break;
                }
            break;

            case 'sql':
                switch($cmdParts[1])
                {
                    case 'build':
                        $callCommandClass = 'SqlBuildCommand';
                    break;

                    case 'insert':
                        $callCommandClass = 'SqlInsertCommand';
                    break;
                }
            break;
        }

        $a = [];
        foreach($this->parameters as $p)
        {
            $x = explode('=', $p);
            if(count($x) > 1)
            {
                $a['--'.str_replace('--', '', $x[0])] = trim($x[1]);
            }
            else
            {
                $a['--'.str_replace('--', '', $x[0])] = true;
            }
        }

        $commandLine = array('command' => $this->command) + $a;

        $app = new Application('Propel', Propel::VERSION);
        $cls = '\Propel\Generator\Command'.'\\'.$callCommandClass;
        /** @noinspection PhpParamsInspection */
        $app->add(new $cls());
        $app->setAutoExit(false);
        $output = new StreamOutput(fopen("php://temp", 'r+'));
        $result = $app->run(new ArrayInput($commandLine), $output);

        if(0 !== $result)
        {
            rewind($output->getStream());
            return stream_get_contents($output->getStream());
        }
        else
        {
            return true;
        }
    }
}

And the usage example:

//Convert the configuration file    
$cmd = new Commander('config:convert', '--config-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/config --output-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/config');
$cmd->run();

//... or (re)build models
$cmd = new Commander('model:build', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/module/schema --output-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/module/models');
$cmd->run();

//... or perform database migration (actually not tested yet :/ )
$cmd = new Commander('migration:diff', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache');
$cmd->preMigrate([$_SERVER['DOCUMENT_ROOT'].'/propeltest/schema', $_SERVER['DOCUMENT_ROOT'].'/propeltest/module/schema'], $_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache');
$cmd->run(); // runs migrate:diff
$cmd->addCommand('migration:diff', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache'); // prepare to actually migration
$cmd->run(); // perform migration:migrate
$cmd->postMigrate();