418 lines
12 KiB
PHP
418 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Orangehill\Iseed;
|
|
|
|
use Illuminate\Filesystem\Filesystem;
|
|
use Illuminate\Support\Composer;
|
|
use Illuminate\Support\Facades\Config;
|
|
|
|
class Iseed
|
|
{
|
|
/**
|
|
* Name of the database upon which the seed will be executed.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $databaseName;
|
|
|
|
/**
|
|
* New line character for seed files.
|
|
* Double quotes are mandatory!
|
|
*
|
|
* @var string
|
|
*/
|
|
private $newLineCharacter = PHP_EOL;
|
|
|
|
/**
|
|
* Desired indent for the code.
|
|
* For tabulator use \t
|
|
* Double quotes are mandatory!
|
|
*
|
|
* @var string
|
|
*/
|
|
private $indentCharacter = " ";
|
|
|
|
/**
|
|
* @var Composer
|
|
*/
|
|
private $composer;
|
|
|
|
public function __construct(Filesystem $filesystem = null, Composer $composer = null)
|
|
{
|
|
$this->files = $filesystem ?: new Filesystem;
|
|
$this->composer = $composer ?: new Composer($this->files);
|
|
}
|
|
|
|
public function readStubFile($file)
|
|
{
|
|
$buffer = file($file, FILE_IGNORE_NEW_LINES);
|
|
return implode(PHP_EOL, $buffer);
|
|
}
|
|
|
|
/**
|
|
* Generates a seed file.
|
|
* @param string $table
|
|
* @param string $prefix
|
|
* @param string $suffix
|
|
* @param string $database
|
|
* @param int $max
|
|
* @param string $prerunEvent
|
|
* @param string $postunEvent
|
|
* @return bool
|
|
* @throws Orangehill\Iseed\TableNotFoundException
|
|
*/
|
|
public function generateSeed($table, $prefix=null, $suffix=null, $database = null, $max = 0, $chunkSize = 0, $exclude = null, $prerunEvent = null, $postrunEvent = null, $dumpAuto = true, $indexed = true, $orderBy = null, $direction = 'ASC')
|
|
{
|
|
if (!$database) {
|
|
$database = config('database.default');
|
|
}
|
|
|
|
$this->databaseName = $database;
|
|
|
|
// Check if table exists
|
|
if (!$this->hasTable($table)) {
|
|
throw new TableNotFoundException("Table $table was not found.");
|
|
}
|
|
|
|
// Get the data
|
|
$data = $this->getData($table, $max, $exclude, $orderBy, $direction);
|
|
|
|
// Repack the data
|
|
$dataArray = $this->repackSeedData($data);
|
|
|
|
// Generate class name
|
|
$className = $this->generateClassName($table, $prefix, $suffix);
|
|
|
|
// Get template for a seed file contents
|
|
$stub = $this->readStubFile($this->getStubPath() . '/seed.stub');
|
|
|
|
// Get a seed folder path
|
|
$seedPath = $this->getSeedPath();
|
|
|
|
// Get a app/database/seeds path
|
|
$seedsPath = $this->getPath($className, $seedPath);
|
|
|
|
// Get a populated stub file
|
|
$seedContent = $this->populateStub(
|
|
$className,
|
|
$stub,
|
|
$table,
|
|
$dataArray,
|
|
$chunkSize,
|
|
$prerunEvent,
|
|
$postrunEvent,
|
|
$indexed
|
|
);
|
|
|
|
// Save a populated stub
|
|
$this->files->put($seedsPath, $seedContent);
|
|
|
|
// Run composer dump-auto
|
|
if ($dumpAuto) {
|
|
$this->composer->dumpAutoloads();
|
|
}
|
|
|
|
// Update the DatabaseSeeder.php file
|
|
return $this->updateDatabaseSeederRunMethod($className) !== false;
|
|
}
|
|
|
|
/**
|
|
* Get a seed folder path
|
|
* @return string
|
|
*/
|
|
public function getSeedPath()
|
|
{
|
|
return base_path() . config('iseed::config.path');
|
|
}
|
|
|
|
/**
|
|
* Get the Data
|
|
* @param string $table
|
|
* @return Array
|
|
*/
|
|
public function getData($table, $max, $exclude = null, $orderBy = null, $direction = 'ASC')
|
|
{
|
|
$result = \DB::connection($this->databaseName)->table($table);
|
|
|
|
if (!empty($exclude)) {
|
|
$allColumns = \DB::connection($this->databaseName)->getSchemaBuilder()->getColumnListing($table);
|
|
$result = $result->select(array_diff($allColumns, $exclude));
|
|
}
|
|
|
|
if($orderBy) {
|
|
$result = $result->orderBy($orderBy, $direction);
|
|
}
|
|
|
|
if ($max) {
|
|
$result = $result->limit($max);
|
|
}
|
|
|
|
return $result->get();
|
|
}
|
|
|
|
/**
|
|
* Repacks data read from the database
|
|
* @param array|object $data
|
|
* @return array
|
|
*/
|
|
public function repackSeedData($data)
|
|
{
|
|
if (!is_array($data)) {
|
|
$data = $data->toArray();
|
|
}
|
|
$dataArray = array();
|
|
if (!empty($data)) {
|
|
foreach ($data as $row) {
|
|
$rowArray = array();
|
|
foreach ($row as $columnName => $columnValue) {
|
|
$rowArray[$columnName] = $columnValue;
|
|
}
|
|
$dataArray[] = $rowArray;
|
|
}
|
|
}
|
|
return $dataArray;
|
|
}
|
|
|
|
/**
|
|
* Checks if a database table exists
|
|
* @param string $table
|
|
* @return boolean
|
|
*/
|
|
public function hasTable($table)
|
|
{
|
|
return \Schema::connection($this->databaseName)->hasTable($table);
|
|
}
|
|
|
|
/**
|
|
* Generates a seed class name (also used as a filename)
|
|
* @param string $table
|
|
* @param string $prefix
|
|
* @param string $suffix
|
|
* @return string
|
|
*/
|
|
public function generateClassName($table, $prefix=null, $suffix=null)
|
|
{
|
|
$tableString = '';
|
|
$tableName = explode('_', $table);
|
|
foreach ($tableName as $tableNameExploded) {
|
|
$tableString .= ucfirst($tableNameExploded);
|
|
}
|
|
return ($prefix ? $prefix : '') . ucfirst($tableString) . 'Table' . ($suffix ? $suffix : '') . 'Seeder';
|
|
}
|
|
|
|
/**
|
|
* Get the path to the stub file.
|
|
* @return string
|
|
*/
|
|
public function getStubPath()
|
|
{
|
|
return __DIR__ . DIRECTORY_SEPARATOR . 'Stubs';
|
|
}
|
|
|
|
/**
|
|
* Populate the place-holders in the seed stub.
|
|
* @param string $class
|
|
* @param string $stub
|
|
* @param string $table
|
|
* @param string $data
|
|
* @param int $chunkSize
|
|
* @param string $prerunEvent
|
|
* @param string $postunEvent
|
|
* @return string
|
|
*/
|
|
public function populateStub($class, $stub, $table, $data, $chunkSize = null, $prerunEvent = null, $postrunEvent = null, $indexed = true)
|
|
{
|
|
$chunkSize = $chunkSize ?: config('iseed::config.chunk_size');
|
|
|
|
$inserts = '';
|
|
$chunks = array_chunk($data, $chunkSize);
|
|
foreach ($chunks as $chunk) {
|
|
$this->addNewLines($inserts);
|
|
$this->addIndent($inserts, 2);
|
|
$inserts .= sprintf(
|
|
"\DB::table('%s')->insert(%s);",
|
|
$table,
|
|
$this->prettifyArray($chunk, $indexed)
|
|
);
|
|
}
|
|
|
|
$stub = str_replace('{{class}}', $class, $stub);
|
|
|
|
$prerunEventInsert = '';
|
|
if ($prerunEvent) {
|
|
$prerunEventInsert .= "\$response = Event::until(new $prerunEvent());";
|
|
$this->addNewLines($prerunEventInsert);
|
|
$this->addIndent($prerunEventInsert, 2);
|
|
$prerunEventInsert .= 'if ($response === false) {';
|
|
$this->addNewLines($prerunEventInsert);
|
|
$this->addIndent($prerunEventInsert, 3);
|
|
$prerunEventInsert .= 'throw new Exception("Prerun event failed, seed wasn\'t executed!");';
|
|
$this->addNewLines($prerunEventInsert);
|
|
$this->addIndent($prerunEventInsert, 2);
|
|
$prerunEventInsert .= '}';
|
|
}
|
|
|
|
$stub = str_replace(
|
|
'{{prerun_event}}', $prerunEventInsert, $stub
|
|
);
|
|
|
|
if (!is_null($table)) {
|
|
$stub = str_replace('{{table}}', $table, $stub);
|
|
}
|
|
|
|
$postrunEventInsert = '';
|
|
if ($postrunEvent) {
|
|
$postrunEventInsert .= "\$response = Event::until(new $postrunEvent());";
|
|
$this->addNewLines($postrunEventInsert);
|
|
$this->addIndent($postrunEventInsert, 2);
|
|
$postrunEventInsert .= 'if ($response === false) {';
|
|
$this->addNewLines($postrunEventInsert);
|
|
$this->addIndent($postrunEventInsert, 3);
|
|
$postrunEventInsert .= 'throw new Exception("Seed was executed but the postrun event failed!");';
|
|
$this->addNewLines($postrunEventInsert);
|
|
$this->addIndent($postrunEventInsert, 2);
|
|
$postrunEventInsert .= '}';
|
|
}
|
|
|
|
$stub = str_replace(
|
|
'{{postrun_event}}', $postrunEventInsert, $stub
|
|
);
|
|
|
|
$stub = str_replace('{{insert_statements}}', $inserts, $stub);
|
|
|
|
return $stub;
|
|
}
|
|
|
|
/**
|
|
* Create the full path name to the seed file.
|
|
* @param string $name
|
|
* @param string $path
|
|
* @return string
|
|
*/
|
|
public function getPath($name, $path)
|
|
{
|
|
return $path . '/' . $name . '.php';
|
|
}
|
|
|
|
/**
|
|
* Prettify a var_export of an array
|
|
* @param array $array
|
|
* @return string
|
|
*/
|
|
protected function prettifyArray($array, $indexed = true)
|
|
{
|
|
$content = ($indexed)
|
|
? var_export($array, true)
|
|
: preg_replace("/[0-9]+ \=\>/i", '', var_export($array, true));
|
|
|
|
$lines = explode("\n", $content);
|
|
|
|
$inString = false;
|
|
$tabCount = 3;
|
|
for ($i = 1; $i < count($lines); $i++) {
|
|
$lines[$i] = ltrim($lines[$i]);
|
|
|
|
//Check for closing bracket
|
|
if (strpos($lines[$i], ')') !== false) {
|
|
$tabCount--;
|
|
}
|
|
|
|
//Insert tab count
|
|
if ($inString === false) {
|
|
for ($j = 0; $j < $tabCount; $j++) {
|
|
$lines[$i] = substr_replace($lines[$i], $this->indentCharacter, 0, 0);
|
|
}
|
|
}
|
|
|
|
for ($j = 0; $j < strlen($lines[$i]); $j++) {
|
|
//skip character right after an escape \
|
|
if ($lines[$i][$j] == '\\') {
|
|
$j++;
|
|
}
|
|
//check string open/end
|
|
else if ($lines[$i][$j] == '\'') {
|
|
$inString = !$inString;
|
|
}
|
|
}
|
|
|
|
//check for openning bracket
|
|
if (strpos($lines[$i], '(') !== false) {
|
|
$tabCount++;
|
|
}
|
|
}
|
|
|
|
$content = implode("\n", $lines);
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Adds new lines to the passed content variable reference.
|
|
*
|
|
* @param string $content
|
|
* @param int $numberOfLines
|
|
*/
|
|
private function addNewLines(&$content, $numberOfLines = 1)
|
|
{
|
|
while ($numberOfLines > 0) {
|
|
$content .= $this->newLineCharacter;
|
|
$numberOfLines--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds indentation to the passed content reference.
|
|
*
|
|
* @param string $content
|
|
* @param int $numberOfIndents
|
|
*/
|
|
private function addIndent(&$content, $numberOfIndents = 1)
|
|
{
|
|
while ($numberOfIndents > 0) {
|
|
$content .= $this->indentCharacter;
|
|
$numberOfIndents--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleans the iSeed section
|
|
* @return bool
|
|
*/
|
|
public function cleanSection()
|
|
{
|
|
$databaseSeederPath = base_path() . config('iseed::config.path') . '/DatabaseSeeder.php';
|
|
|
|
$content = $this->files->get($databaseSeederPath);
|
|
|
|
$content = preg_replace("/(\#iseed_start.+?)\#iseed_end/us", "#iseed_start\n\t\t#iseed_end", $content);
|
|
|
|
return $this->files->put($databaseSeederPath, $content) !== false;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Updates the DatabaseSeeder file's run method (kudoz to: https://github.com/JeffreyWay/Laravel-4-Generators)
|
|
* @param string $className
|
|
* @return bool
|
|
*/
|
|
public function updateDatabaseSeederRunMethod($className)
|
|
{
|
|
$databaseSeederPath = base_path() . config('iseed::config.path') . '/DatabaseSeeder.php';
|
|
|
|
$content = $this->files->get($databaseSeederPath);
|
|
if (strpos($content, "\$this->call({$className}::class)") === false) {
|
|
if (
|
|
strpos($content, '#iseed_start') &&
|
|
strpos($content, '#iseed_end') &&
|
|
strpos($content, '#iseed_start') < strpos($content, '#iseed_end')
|
|
) {
|
|
$content = preg_replace("/(\#iseed_start.+?)(\#iseed_end)/us", "$1\$this->call({$className}::class);{$this->newLineCharacter}{$this->indentCharacter}{$this->indentCharacter}$2", $content);
|
|
} else {
|
|
$content = preg_replace("/(run\(\).+?)}/us", "$1{$this->indentCharacter}\$this->call({$className}::class);{$this->newLineCharacter}{$this->indentCharacter}}", $content);
|
|
}
|
|
}
|
|
|
|
return $this->files->put($databaseSeederPath, $content) !== false;
|
|
}
|
|
}
|