Skip to content

Commit

Permalink
Implement file handling and the PhpHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
MGatner committed Sep 19, 2019
1 parent 8465255 commit 619eb2b
Show file tree
Hide file tree
Showing 21 changed files with 347 additions and 71 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ $schemas = service('schemas');
$this->schemas->import('database')->export('cache');
// Load the schema from cache, add Model data, and get the updated schema
$schema = $this->schemas->import('cache', 'model')->get();
$schema = $this->schemas->import(['cache', 'model'])->get();
```

If you need to deviate from the default configuration you can inject the handlers yourself:
Expand All @@ -73,6 +73,7 @@ Current supported handlers:
* Database (import only)
* Model (import only)
* Cache
* FileHandler (PHP import only)

### Development

Expand All @@ -82,7 +83,11 @@ implementations include:
* `DatabaseHandler->export()`: Recreate a live database from its schema
* `MigrationsHandler`: Create a schema from migration files, or vice versa
* `FileHandler`: A wrapper for importing and exporting from popular schema file formats
* `XmlHandler`: The first file handler, supporting Doctrine-style XML files
* More to come...

And the file-specific handlers:
* `PhpHandler->export()`: Create a PHP file with a Schema object in `$schema`
* `XmlHandler`: Support for Doctrine-style XML files
* More to come...

Want to help out? All code and issues are managed on GitHub at [Tatter\Schemas](https://github.com/tattersoftware/codeigniter4-schemas)
3 changes: 3 additions & 0 deletions bin/Schemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ class Schemas extends \Tatter\Schemas\Config\Schemas

// Default time-to-live for a stored schema (e.g. Cache)
public $ttl = 6000;

// Path to a folder to scan for schema files
public $schemasDirectory = APPPATH . 'Schemas';
}
2 changes: 1 addition & 1 deletion src/Commands/Schemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function run(array $handlers = [])
// Try the import
try
{
$schemas->import(...$handlers);
$schemas->import($handlers);
}
catch (\Exception $e)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Config/Schemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ class Schemas extends BaseConfig

// Default time-to-live for a stored schema (e.g. Cache)
public $ttl = 6000;

// Path to a folder to scan for schema files
public $schemasDirectory = APPPATH . 'Schemas';
}
73 changes: 73 additions & 0 deletions src/Handlers/BaseHandler.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?php namespace Tatter\Schemas\Handlers;

use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Files\Exceptions\FileNotFoundException;
use CodeIgniter\Files\File;
use Tatter\Schemas\Exceptions\SchemasException;
use Tatter\Schemas\Interfaces\SchemaHandlerInterface;
use Tatter\Schemas\Structures\Schema;
use Tatter\Schemas\Structures\Relation;
use Tatter\Schemas\Structures\Table;
Expand Down Expand Up @@ -116,4 +119,74 @@ protected function methodNotImplemented(string $class, string $method)

$this->errors[] = lang('Schemas.methodNotImplemented', [$class, $method]);
}

// COMMON FUNCTIONS FOR FILE HANDLERS

/**
* Validate a file and get its contents.
*
* @param string $path The path to the file
*
* @throws
*/
protected function getContents($path): ?string
{
$file = new File($path, $this->config->silent); // if not silent will throw for missing files

if (! $file->isFile())
{
$this->errors[] = lang('Files.fileNotFound', [$path]);
return null;
}

return file_get_contents($file->getRealPath());
}

/**
* Validate or create a file and write to it.
*
* @param string $path The path to the file
*
* @throws
*/
protected function putContents($path, string $data): bool
{
$file = new File($path);

if (! $file->isWritable())
{
if ($this->config->silent)
{
$this->errors[] = lang('Files.fileNotFound', [$path]);
return null;
}
else
{
throw FileNotFoundException::forFileNotFound($path);
}
}

$file = $file->openFile('w');
return (bool)$file->fwrite($data);
}

/**
* Tries to match a file extension to its handler
*
* @return SchemaHandlerInterface
*/
protected function getHandlerForFile(string $path): ?SchemaHandlerInterface
{
$extension = pathinfo($path, PATHINFO_EXTENSION);

$class = '\Tatter\Schemas\Handlers\\' . ucfirst(strtolower($extension)) . 'Handler';

if (! class_exists($class))
{
return null;
}

$handler = new $class($this->config, $path);
return $handler;
}
}
91 changes: 31 additions & 60 deletions src/Handlers/FileHandler.php
Original file line number Diff line number Diff line change
@@ -1,85 +1,56 @@
<?php namespace Tatter\Schemas\Handlers;

use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Files\Exceptions\FileNotFoundException;
use Tatter\Schemas\Exceptions\SchemasException;
use Tatter\Schemas\Structures\Schema;

class FileHandler extends BaseHandler
{
/**
* The file path.
*
* @var string
*/
protected $path;

// Initiate library
public function __construct(BaseConfig $config = null, $path = null)
{
parent::__construct($config);

// Guess at a default file location
if (is_null($file))
{
$this->file = WRITEPATH . 'uploads/schema.xml';
}
// Save injected file path
else
{
$this->path = $path;
}

}

// Change the path
public function setPath(string $path)
public function __construct(BaseConfig $config = null)
{
$this->path = $path;
return $this;
}

// Get the path
public function getPath()
{
return $this->path;
parent::__construct($config);
}

// Validate the current file and get its contents
public function getContents(): ?string
// Scan the schemas directory and process any files found
public function get(): ?Schema
{
if (! is_file($this->path))
helper('filesystem');
$files = get_filenames($this->config->schemasDirectory, true);

if (empty($files))
{
if ($this->config->silent)
{
$this->errors[] = lang('Files.fileNotFound', [$this->path]);
return null;
}
else
{
throw FileNotFoundException::forFileNotFound($this->path);
}
$this->errors[] = lang('Schemas.emptySchemaDirectory', [$this->config->schemasDirectory]);
return null;
}

return file_get_contents($this->path);
}

// Validate the target file and write to it
public function putContents(string $data): bool
{
if (! is_file($this->path))
// Try each file
foreach ($files as $path)
{
// WIP - should try to create the file
if ($this->config->silent)
// Make sure there is a handler for this extension
$handler = $this->getHandlerForFile($path);
if (is_null($handler))
{
$this->errors[] = lang('Schemas.unsupportedHandler', [pathinfo($path, PATHINFO_EXTENSION)]);
continue;
}

if (empty($schema))
{
$this->errors[] = lang('Files.fileNotFound', [$this->path]);
return null;
$schema = $handler->get();
}
else
{
throw FileNotFoundException::forFileNotFound($this->path);
$schema->merge($handler->get());
}
}

return (bool)file_put_contents($this->path, $data);
return $schema ?? null;
}

public function save(Schema $schema): bool
{
$this->methodNotImplemented(__CLASS__, 'save');
return false;
}
}
59 changes: 59 additions & 0 deletions src/Handlers/PhpHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php namespace Tatter\Schemas\Handlers;

use CodeIgniter\Config\BaseConfig;
use Tatter\Schemas\Exceptions\SchemasException;
use Tatter\Schemas\Interfaces\SchemaHandlerInterface;
use Tatter\Schemas\Structures\Schema;
use Tatter\Schemas\Structures\Relation;
use Tatter\Schemas\Structures\Table;
use Tatter\Schemas\Structures\Field;
use Tatter\Schemas\Structures\Index;
use Tatter\Schemas\Structures\ForeignKey;

class PhpHandler extends BaseHandler implements SchemaHandlerInterface
{
/**
* The file path.
*
* @var string
*/
protected $path;

// Initiate library
public function __construct(BaseConfig $config = null, $path = null)
{
parent::__construct($config);

// Save the path
$this->path = $path;
}

// Read in data from the file and fit it into a schema
public function get(): ?Schema
{
$contents = $this->getContents($this->path);
if (is_null($contents))
{
$this->errors[] = lang('Schemas.emptySchemaFile', [$this->path]);
return null;
}

// PHP files should contain pre-built schemas in the $schema variable
// So the path just needs to be included and the variable checked
try {
require $this->path;
} catch (\Exception $e) {
$this->errors[] = $e->getMessage();
return null;
}

return $schema ?? null;
}

// Write out the schema to file
public function save(Schema $schema): bool
{
$this->methodNotImplemented(__CLASS__, 'save');
return false;
}
}
2 changes: 1 addition & 1 deletion src/Handlers/XmlHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Tatter\Schemas\Structures\Index;
use Tatter\Schemas\Structures\ForeignKey;

class XmlHandler extends FileHandler implements SchemaHandlerInterface
class XmlHandler extends BaseHandler implements SchemaHandlerInterface
{
/**
* The file path.
Expand Down
2 changes: 2 additions & 0 deletions src/Language/en/Schemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
'methodNotImplemented' => '{0} does not have a {1} method',
'unsupportedHandler' => 'Handler not supported: {0}',
'missingField' => '{0} is missing {1}',
'emptySchemaFile' => 'Could not read file contents from {0}',
'emptySchemaDirectory' => 'No files found in schema directory: {0}',
];
8 changes: 7 additions & 1 deletion src/Schemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,14 @@ public function getErrors(): array
*
* @return $this
*/
public function import(...$handlers)
public function import($handlers)
{
// Wrap singletons
if (! is_array($handlers))
{
$handlers = [$handlers];
}

// Import from each handler in order
foreach ($handlers as $handler)
{
Expand Down
27 changes: 26 additions & 1 deletion src/Structures/Mergeable.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,37 @@ public function merge(?Mergeable $object): Mergeable
}

/**
* Specify count of public properties to satisfy Countable
* Specify count of public properties to satisfy Countable.
*
* @int Number of public properties
*/
public function count(): int
{
return count(get_object_vars($this));
}

/**
* Magic getter to prevent exceptions on missing property checks.
*
* @mixed
*/
public function __get($name)
{
if (property_exists($this, $name))
{
return $this->$name;
}

return null;
}

/**
* Magic checker to match the getter.
*
* @bool
*/
public function __isset($name): bool
{
return property_exists($this, $name);
}
}
5 changes: 0 additions & 5 deletions src/Structures/Relation.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,4 @@ class Relation extends Mergeable
* @var Array of [tableName, fieldName, foreignField]
*/
public $pivots = [];

public function __construct()
{
$this->pivots = new Mergeable();
}
}
Loading

0 comments on commit 619eb2b

Please sign in to comment.