Last active
September 25, 2023 16:18
-
-
Save kmuenkel/9a05a88677c9a4982ecfd813a7eb3713 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Database\Seeders; | |
use Illuminate\Support\Arr; | |
use Illuminate\Database\Eloquent\Model; | |
use Illuminate\Support\Facades\{DB, Schema}; | |
/** | |
* @property string|Model $model | |
* @property string $csvName | |
* @property string $resourceRootPath | |
* @property string[] $fieldMap | |
*/ | |
trait CsvSeeder | |
{ | |
protected function getModel(): Model | |
{ | |
return is_string($this->model) ? app($this->model) : $this->model; | |
} | |
protected function getCsvName(): string | |
{ | |
return property_exists($this, 'csvName') ? $this->csvName : $this->getModel()->getTable() . '.csv'; | |
} | |
protected function getResourcePath(): string | |
{ | |
return trim(property_exists($this, 'resourceRootPath') ? $this->resourceRootPath : __DIR__ . '/../seeds', '/'); | |
} | |
/** | |
* @return false|resource | |
*/ | |
protected function getHandle() | |
{ | |
$path = '/' . $this->getResourcePath() . '/' . $this->getCsvName(); | |
return fopen($path, 'r'); | |
} | |
public function run(): void | |
{ | |
$handle = $this->getHandle(); | |
$headers = fgetcsv($handle); | |
try { | |
$this->disableStrictMode(); | |
$this->openTableForSeeding(); | |
$this->performSeed($handle, $headers); | |
} finally { | |
fclose($handle); | |
$this->closeTableForSeeding(); | |
} | |
} | |
/** | |
* @param resource $handle | |
*/ | |
protected function performSeed($handle, array $headers): void | |
{ | |
$rowNum = 0; | |
while (!$this->rowIsEmpty($cells = fgetcsv($handle, 0, ',', '"', '"'))) { | |
if (!$this->hasPopulatedCells($cells)) { //This could come up if the row is all commas | |
break; | |
} | |
$row = $this->mutateRow(array_combine($headers, $cells)); | |
$this->saveRow($row, $rowNum++); | |
} | |
} | |
protected function disableStrictMode(): void | |
{ | |
$connectionConfig = config('database.connections.' . config('database.default')); | |
$connectionConfig['strict'] = false; //Allow for "0000-00-00" date values | |
config(['database.connections.' . config('database.default') => $connectionConfig]); | |
} | |
protected function openTableForSeeding(): void | |
{ | |
$this->getModel()::unguard(); | |
Schema::disableForeignKeyConstraints(); | |
} | |
protected function closeTableForSeeding(): void | |
{ | |
Schema::enableForeignKeyConstraints(); | |
$this->getModel()::reguard(); | |
} | |
protected function hasPopulatedCells($cells): bool | |
{ | |
return (bool)array_filter($cells, fn ($cell) => $cell !== ''); | |
} | |
protected function rowIsEmpty($cells): bool | |
{ | |
return in_array($cells, [false, [null]], true); | |
} | |
/** | |
* Allow for final row alteration to be performed by the child class. | |
* | |
* @param array $row | |
* @return array | |
*/ | |
protected function mutateRow(array $row): array | |
{ | |
return $row; | |
} | |
/** | |
* @param array $row | |
* @param int $rowNum | |
* @return void | |
*/ | |
protected function saveRow(array $row, int $rowNum): void | |
{ | |
//Preempt type failures stemming from how CSVs don't typically differentiate between empty strings and nulls | |
array_walk($row, fn (&$value) => $value = in_array($value, ['NULL', ''], true) ? null : $value); | |
// $this->getModel()->timestamps && Arr::forget($row, [$this->model::CREATED_AT, $this->model::UPDATED_AT]); | |
// in_array(SoftDeletes::class, class_uses_recursive($this->model)) && Arr::forget($row, [$this->model->getDeletedAtColumn()]); | |
$builder = DB::table($this->getModel()->getTable()); //Circumvent any mutators that may inhibit the raw data | |
$keyName = $this->getModel()->getKeyName(); //We only need the Model configs, not its behavior | |
$key = Arr::get($row, $keyName); | |
$exists = $key && $builder->where($keyName, $key)->exists(); | |
$exists ? $builder->where($keyName, $key)->update($row) : $builder->insert($row); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Database\Seeders; | |
use DB; | |
use Schema; | |
use stdClass; | |
use Illuminate\Database\Eloquent\Model; | |
use Illuminate\Database\QueryException; | |
trait ResetIndexes | |
{ | |
/** | |
* @param Model $model | |
*/ | |
public function reset(Model $model) | |
{ | |
$constraints = !app()->runningUnitTests() ? DB::table('INFORMATION_SCHEMA.KEY_COLUMN_USAGE') | |
->select(['TABLE_NAME', 'COLUMN_NAME']) | |
->where([ | |
'REFERENCED_TABLE_SCHEMA' => $model->getConnection()->getDatabaseName(), | |
'REFERENCED_TABLE_NAME' => $model->getTable(), | |
'REFERENCED_COLUMN_NAME' => $model->getKeyName() | |
]) | |
->get() : collect(); | |
if (!($adjustment = $model->min($model->getKeyName()) - 1)) { | |
return; | |
} | |
$adjustmentValue = DB::raw('`'.$model->getKeyName()."` - $adjustment"); | |
try { | |
!app()->runningUnitTests() && Schema::disableForeignKeyConstraints(); | |
DB::table($model->getTable())->update([$model->getKeyName() => $adjustmentValue]); | |
} finally { | |
!app()->runningUnitTests() && Schema::enableForeignKeyConstraints(); | |
} | |
$aiValue = $model->max($model->getKeyName()) + 1; | |
try { | |
DB::statement("ALTER TABLE " . $model->getTable() . " AUTO_INCREMENT = $aiValue"); | |
} catch (QueryException $exception) { | |
// If this DB user doesn't have permission to do this, just don't worry about it | |
} | |
try { | |
!app()->runningUnitTests() && Schema::disableForeignKeyConstraints(); | |
$constraints->each(function (stdClass $result) use ($adjustment) { | |
$adjustmentValue = DB::raw('`' . $result->COLUMN_NAME . "` - $adjustment"); | |
DB::table($result->TABLE_NAME)->update([$result->COLUMN_NAME => $adjustmentValue]); | |
}); | |
} finally { | |
!app()->runningUnitTests() && Schema::enableForeignKeyConstraints(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment