Skip to content

Commit

Permalink
Phalcon adapter and migration task
Browse files Browse the repository at this point in the history
  • Loading branch information
tzatrepalek-inwk committed Jan 22, 2019
1 parent 0111e46 commit 34cfdf2
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"nette/tester": "~1.7 | ~2.0",
"nette/utils": "~2.3",
"nextras/dbal": "~1.0 | ~2.0 | ~3.0",
"phalcon/ide-stubs": "~3.4",
"symfony/config": "~2.6 | ~3.0 | ~4.0",
"symfony/console": "~2.6 | ~3.0 | ~4.0",
"symfony/dependency-injection": "~2.6 | ~3.0 | ~4.0",
Expand Down
245 changes: 245 additions & 0 deletions src/Bridges/Phalcon/MigrationsTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
<?php

namespace Nextras\Migrations\Bridges\Phalcon;

use Nextras\Migrations\Controllers\ConsoleController;
use Nextras\Migrations\Extensions\SqlHandler;
use Nextras\Migrations\IDriver;
use Phalcon\Cli\Task;
use Phalcon\Di\FactoryDefault\Cli;

class MigrationsTask extends Task
{
/** @var string|null */
private $migrationsDir;

/** @var IDriver|null */
private $driver;

/** @var Cli|null */
private $di;

const ACTION_CREATE = 'create';
const ACTION_CREATE_ABBR = 'cr';
const ACTION_RESET = 'reset';
const ACTION_RESET_ABBR = 're';
const ACTION_CONTINUE = 'continue';
const ACTION_CONTINUE_ABBR = 'co';

const GROUP_BASIC_DATA = 'basic-data';
const GROUP_BASIC_DATA_ABBR = 'b';
const GROUP_DUMMY_DATA = 'dummy-data';
const GROUP_DUMMY_DATA_ABBR = 'd';
const GROUP_STRUCTURES = 'structures';
const GROUP_STRUCTURES_ABBR = 's';

/** @var array */
private static $groups = [
self::GROUP_BASIC_DATA_ABBR => self::GROUP_BASIC_DATA,
self::GROUP_DUMMY_DATA_ABBR => self::GROUP_DUMMY_DATA,
self::GROUP_STRUCTURES_ABBR => self::GROUP_STRUCTURES,
];

private function getContainer()
{
if ($this->di === null) {
$this->di = $this->getDI();
}

return $this->di;
}

/**
* @return string
*/
private function getMigrationsDir()
{
if ($this->migrationsDir === null) {
$this->migrationsDir = $this->getContainer()->get('config')->migrationsDir;
}

return $this->migrationsDir;
}

/**
* @return IDriver
*/
private function getDriver()
{
if ($this->driver === null) {
$this->driver = $this->getContainer()->get('driver');
}
return $this->driver;
}

/**
* @param array $params
*/
public function mainAction($params)
{
error_reporting(E_ALL);
ini_set('display_errors', 1);

try {
$params = $this->handleParams($params);
} catch (\InvalidArgumentException $e) {
$this->error($e->getMessage());
}

$controller = new ConsoleController($this->getDriver());

$controller->addGroup('structures', $this->getMigrationsDir() . '/structures');
$controller->addGroup('basic-data', $this->getMigrationsDir() . '/basic-data', ['structures']);
$controller->addGroup('dummy-data', $this->getMigrationsDir() . '/dummy-data', ['basic-data']);
$controller->addExtension('sql', new SqlHandler($this->getDriver()));

// override parameters
$_SERVER['argv'] = array_merge([$_SERVER['argv'][0]], $params);

$controller->run();
}

/**
* @param array $params
* @return array
* @throws \InvalidArgumentException
*/
private function handleParams($params)
{
if (count($params) !== 1) {
throw new \InvalidArgumentException('Invalid number of params.');
}

$param_parts = explode(':', $params[0]);
$count = count($param_parts);

// continue or reset
if ($count > 0 && $count < 3) {
$production = false;
if (isset($param_parts[1])) {
if ($param_parts[1] === 'production') {
$production = true;
} else {
throw new \InvalidArgumentException('Invalid params.');
}
}

return $this->runMigrations($param_parts[0], $production);
} elseif ($count === 3) {
// create
if (!in_array($param_parts[0], [self::ACTION_CREATE, self::ACTION_CREATE_ABBR], true)) {
throw new \InvalidArgumentException('Invalid params.'); // 3 arguments can have only create action
}

$this->createMigration($param_parts[1], $param_parts[2]);
}

throw new \InvalidArgumentException('Invalid params.');
}

/**
* @param string $message
*/
private function error($message)
{
echo 'ERROR: ' . $message . PHP_EOL;
$this->printUsage();
exit(1);
}

/**
* @param string $action
* @param bool $production
* @return array
* @throws \InvalidArgumentException
*/
private function runMigrations($action, $production)
{
if (!in_array($action, [self::ACTION_CONTINUE, self::ACTION_CONTINUE_ABBR, self::ACTION_RESET, self::ACTION_RESET_ABBR], true)) {
throw new \InvalidArgumentException('Invalid action.');
}

$return = [self::GROUP_STRUCTURES, self::GROUP_BASIC_DATA];

if ($production === false) {
$return[] = self::GROUP_DUMMY_DATA;
}

if (in_array($action, [self::ACTION_RESET, self::ACTION_RESET_ABBR], true)) {
$return[] = '--reset';
}

return $return;
}

/**
* @param string $group
* @param string $label
* @throws \InvalidArgumentException
*/
private function createMigration($group, $label)
{
if (!in_array($group, array_merge(array_keys(self::$groups), array_values(self::$groups)), true)) {
throw new \InvalidArgumentException('Invalid group.');
}

// replace group abbreviation for group name
if (array_key_exists($group, self::$groups)) {
$group = self::$groups[$group];
}

$dir = $this->getMigrationsDir() . '/' . $group;
$name = date('Y-m-d-His-') . preg_replace('/[[:^print:]]/', '', $label) . '.sql';
@mkdir($dir, 0777, true);
touch("$dir/$name");

exit;
}

private function printUsage()
{
$scriptFile = isset($_SERVER['argv'][0]) ? $_SERVER['argv'][0] : 'app/cli.php';

echo '------------------------------' . PHP_EOL;
echo 'Usage: php ' . $scriptFile . ' Nextras\\\\Migrations\\\\Bridges\\\\Phalcon\\\\Migrations main <action>[:<group>:<label>][:production]' . PHP_EOL;
echo PHP_EOL;
echo 'Example: php ' . $scriptFile . ' Nextras\\\\Migrations\\\\Bridges\\\\Phalcon\\\\Migrations main create:dummy-data:users' . PHP_EOL;
echo 'Example: php ' . $scriptFile . ' Nextras\\\\Migrations\\\\Bridges\\\\Phalcon\\\\Migrations main cr:d:users' . PHP_EOL;
echo 'Example: php ' . $scriptFile . ' Nextras\\\\Migrations\\\\Bridges\\\\Phalcon\\\\Migrations main reset' . PHP_EOL;
echo 'Example: php ' . $scriptFile . ' Nextras\\\\Migrations\\\\Bridges\\\\Phalcon\\\\Migrations main co:production' . PHP_EOL;
echo PHP_EOL;
echo 'Actions:' . PHP_EOL;
echo ' create' . PHP_EOL;
echo ' Can be aliased as "cr".' . PHP_EOL;
echo ' Creates empty sql file named YYYY-MM-DD-HHMMSS-label.sql.' . PHP_EOL;
echo ' E.g. 2015-03-16-170342-users.sql.' . PHP_EOL;
echo ' <label> is mandatory for "create" action.' . PHP_EOL;
echo ' continue' . PHP_EOL;
echo ' Can be aliased as "co".' . PHP_EOL;
echo ' Migrates not migrated sql files only.' . PHP_EOL;
echo ' Optional flag "production" (if present all dummy-data files are skipped).' . PHP_EOL;
echo ' reset' . PHP_EOL;
echo ' Can be aliased as "re".' . PHP_EOL;
echo ' Drop whole database and then migrates all sql files.' . PHP_EOL;
echo ' Optional flag "production" (if present all dummy-data files are skipped).' . PHP_EOL;
echo PHP_EOL;
echo 'Groups:' . PHP_EOL;
echo ' basic-data' . PHP_EOL;
echo ' Can be aliased as "b".' . PHP_EOL;
echo ' Data for both development and production.' . PHP_EOL;
echo ' dummy-data' . PHP_EOL;
echo ' Can be aliased as "d".' . PHP_EOL;
echo ' Data for development on localhost.' . PHP_EOL;
echo ' structures' . PHP_EOL;
echo ' Can be aliased as "s".' . PHP_EOL;
echo ' Creates, alter tables, etc.' . PHP_EOL;
echo PHP_EOL;
echo 'Label:' . PHP_EOL;
echo ' For "create" action only. Should be some brief name for sql file contents.' . PHP_EOL;
echo PHP_EOL;
echo 'Production:' . PHP_EOL;
echo ' For "continue" and "reset" actions only.' . PHP_EOL;
echo ' If present all dummy-data files are skipped.' . PHP_EOL;
echo '------------------------------' . PHP_EOL;
}
}
80 changes: 80 additions & 0 deletions src/Bridges/Phalcon/PhalconAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Nextras\Migrations\Bridges\Phalcon;

use DateTime;
use Nextras\Migrations\ExecutionException;
use Nextras\Migrations\IDbal;
use Phalcon\Db\Adapter\Pdo;


class PhalconAdapter implements IDbal
{
/** @var string Value from \Phalcon\Db\Adapter\Pdo\Mysql::$_type */
const TYPE_MYSQL = 'mysql';

/** @var string Value from \Phalcon\Db\Adapter\Pdo\Postgresql::$_type */
const TYPE_PGSQL = 'pgsql';

/** @var Pdo */
private $conn;


public function __construct(Pdo $connection)
{
$this->conn = $connection;
$this->conn->connect();
}


public function query($sql)
{
return $this->conn->fetchAll($sql);
}


public function exec($sql)
{
$this->conn->execute($sql);
$this->conn->affectedRows();
}


public function escapeString($value)
{
return $this->conn->escapeString($value);
}


public function escapeInt($value)
{
return (string)(int)$value;
}


public function escapeBool($value)
{
return (string)(int)$value;
}


public function escapeDateTime(DateTime $value)
{
return $this->conn->escapeString($this->formatDateTime($value));
}


public function escapeIdentifier($value)
{
return $this->conn->escapeIdentifier($value);
}

private function formatDateTime(DateTime $value)
{
if (in_array($this->conn->getType(), [self::TYPE_MYSQL, self::TYPE_PGSQL], true)) {
return $value->format('Y-m-d H:i:s');
} else {
throw new ExecutionException('Unsupported type of \Phalcon\Db\Adapter\Pdo driver.');
}
}
}

0 comments on commit 34cfdf2

Please sign in to comment.