<?php
namespace Reliese\Meta\Sqlite;
use Reliese\Meta\Blueprint;
use Illuminate\Support\Fluent;
use Illuminate\Database\Connection;
/**
* Created by Cristian.
* Date: 18/09/16 06:50 PM.
*/
class Schema implements \Reliese\Meta\Schema
{
/**
* @var string
*/
protected $schema;
/**
* @var \Illuminate\Database\SQLiteConnection
*/
protected $connection;
/**
* @var bool
*/
protected $loaded = false;
/**
* @var \Reliese\Meta\Blueprint[]
*/
protected $tables = [];
/**
* Mapper constructor.
*
* @param string $schema
* @param \Illuminate\Database\MySqlConnection $connection
*/
public function __construct($schema, $connection)
{
$this->schema = $schema;
$this->connection = $connection;
/* Sqlite has a bool type that doctrine isn't registering */
$this->connection->getDoctrineConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('bool', 'boolean');
$this->load();
}
/**
* @return \Doctrine\DBAL\Schema\AbstractSchemaManager
* @todo: Use Doctrine instead of raw database queries
*/
public function manager()
{
return $this->connection->getDoctrineSchemaManager();
}
/**
* Loads schema's tables' information from the database.
*/
protected function load()
{
$tables = $this->fetchTables();
foreach ($tables as $table) {
$blueprint = new Blueprint($this->connection->getName(), $this->schema, $table);
$this->fillColumns($blueprint);
$this->fillConstraints($blueprint);
$this->tables[$table] = $blueprint;
}
}
/**
* @return array
* @internal param string $schema
*/
protected function fetchTables()
{
$names = $this->manager()->listTableNames();
return array_diff($names, [
'sqlite_master',
'sqlite_sequence',
'sqlite_stat1',
]);
}
/**
* @param \Reliese\Meta\Blueprint $blueprint
*/
protected function fillColumns(Blueprint $blueprint)
{
$columns = $this->manager()->listTableColumns($blueprint->table());
foreach ($columns as $column) {
$blueprint->withColumn(
$this->parseColumn($column)
);
}
}
/**
* @param \Doctrine\DBAL\Schema\Column $metadata
*
* @return \Illuminate\Support\Fluent
*/
protected function parseColumn($metadata)
{
return (new Column($metadata))->normalize();
}
/**
* @param \Reliese\Meta\Blueprint $blueprint
*/
protected function fillConstraints(Blueprint $blueprint)
{
$this->fillPrimaryKey($blueprint);
$this->fillIndexes($blueprint);
$this->fillRelations($blueprint);
}
/**
* Quick little hack since it is no longer possible to set PDO's fetch mode
* to PDO::FETCH_ASSOC.
*
* @param $data
* @return mixed
*/
protected function arraify($data)
{
return json_decode(json_encode($data), true);
}
/**
* @param \Reliese\Meta\Blueprint $blueprint
* @todo: Support named primary keys
*/
protected function fillPrimaryKey(Blueprint $blueprint)
{
$indexes = $this->manager()->listTableIndexes($blueprint->table());
$key = [
'name' => 'primary',
'index' => '',
'columns' => optional($indexes['primary']??null)->getColumns()?:[],
];
$blueprint->withPrimaryKey(new Fluent($key));
}
/**
* @param \Reliese\Meta\Blueprint $blueprint
* @internal param string $sql
*/
protected function fillIndexes(Blueprint $blueprint)
{
$indexes = $this->manager()->listTableIndexes($blueprint->table());
unset($indexes['primary']);
foreach ($indexes as $setup) {
$index = [
'name' => $setup->isUnique() ? 'unique' : 'index',
'columns' => $setup->getColumns(),
'index' => $setup->getName(),
];
$blueprint->withIndex(new Fluent($index));
}
}
/**
* @param \Reliese\Meta\Blueprint $blueprint
* @todo: Support named foreign keys
*/
protected function fillRelations(Blueprint $blueprint)
{
$relations = $this->manager()->listTableForeignKeys($blueprint->table());
foreach ($relations as $setup) {
$table = ['database' => '', 'table'=>$setup->getForeignTableName()];
$relation = [
'name' => 'foreign',
'index' => '',
'columns' => $setup->getColumns(),
'references' => $setup->getForeignColumns(),
'on' => $table,
];
$blueprint->withRelation(new Fluent($relation));
}
}
/**
* @param \Illuminate\Database\Connection $connection
*
* @return array
*/
public static function schemas(Connection $connection)
{
return ['database'];
}
/**
* @return string
*/
public function schema()
{
return $this->schema;
}
/**
* @param string $table
*
* @return bool
*/
public function has($table)
{
return array_key_exists($table, $this->tables);
}
/**
* @return \Reliese\Meta\Blueprint[]
*/
public function tables()
{
return $this->tables;
}
/**
* @param string $table
*
* @return \Reliese\Meta\Blueprint
*/
public function table($table)
{
if (! $this->has($table)) {
throw new \InvalidArgumentException("Table [$table] does not belong to schema [{$this->schema}]");
}
return $this->tables[$table];
}
/**
* @return \Illuminate\Database\MySqlConnection
*/
public function connection()
{
return $this->connection;
}
/**
* @param \Reliese\Meta\Blueprint $table
*
* @return array
*/
public function referencing(Blueprint $table)
{
$references = [];
foreach ($this->tables as $blueprint) {
foreach ($blueprint->references($table) as $reference) {
$references[] = [
'blueprint' => $blueprint,
'reference' => $reference,
];
}
}
return $references;
}
}