diff --git a/composer.json b/composer.json
index e35ce6d..c22546d 100644
--- a/composer.json
+++ b/composer.json
@@ -23,6 +23,7 @@
"require-dev": {
"blitz-php/coding-standard": "^1.5",
"blitz-php/framework": "^1.0.0-rc",
+ "fakerphp/faker": "^1.24",
"kahlan/kahlan": "^6.1.0",
"phpstan/phpstan": "^2.1.36"
},
diff --git a/src/Commands/Seed.php b/src/Commands/Seed.php
index 691fb2b..875d852 100644
--- a/src/Commands/Seed.php
+++ b/src/Commands/Seed.php
@@ -11,7 +11,7 @@
namespace BlitzPHP\Database\Commands;
-use BlitzPHP\Container\Services;
+use BlitzPHP\Database\Connection\BaseConnection;
use BlitzPHP\Database\Seeder\Seeder;
use InvalidArgumentException;
@@ -34,55 +34,138 @@ class Seed extends DatabaseCommand
* {@inheritDoc}
*/
protected array $arguments = [
- 'name' => 'Nom du seedr a executer',
+ 'name' => 'Nom du seeder à exécuter (ex: DatabaseSeeder ou Users\\UserSeeder)',
];
+ /**
+ * {@inheritDoc}
+ */
+ protected array $options = [
+ '--group' => 'Groupe de connexion à utiliser',
+ '--silent' => 'Mode silencieux (pas de sortie)',
+ '--locale' => 'Langue à utiliser pour Faker (ex: fr_FR, en_US)',
+ ];
+
+ protected ?BaseConnection $db = null;
+
/**
* {@inheritDoc}
*/
public function handle()
{
- if (empty($name = $this->argument('name'))) {
- $name = $this->prompt(lang('Migrations.migSeeder'), null, static function ($val) {
+ $group = $this->option('group');
+ $silent = $this->option('silent') !== null;
+ $locale = $this->option('locale', config('app.language', 'fr_FR'));
+
+ $this->db = $this->resolver->connect($group);
+
+ $name = $this->getSeederName();
+
+ $seeder = $this->resolveSeeder($name);
+
+ $this->configureSeeder($seeder, $locale, $silent);
+
+ $this->runSeeder($seeder);
+
+ return EXIT_SUCCESS;
+ }
+
+ /**
+ * Récupère le nom du seeder
+ */
+ protected function getSeederName(): string
+ {
+ if (null !== $name = $this->argument('name')) {
+ return $name;
+ }
+
+ return $this->prompt(
+ 'Quel seeder souhaitez-vous exécuter ?',
+ 'DatabaseSeeder',
+ function ($val) {
if (empty($val)) {
throw new InvalidArgumentException('Veuillez entrer le nom du seeder.');
}
-
return $val;
- });
- }
+ }
+ );
+ }
- $seedClass = APP_NAMESPACE . '\Database\Seeds\\';
- $seedClass .= str_replace($seedClass, '', $name);
+ /**
+ * Résout la classe du seeder
+ */
+ protected function resolveSeeder(string $name): Seeder
+ {
+ // Chemins possibles
+ $paths = [
+ APP_NAMESPACE . '\\Database\\Seeds\\',
+ APP_NAMESPACE . '\\Database\\Seeders\\',
+ 'Database\\Seeds\\',
+ 'Database\\Seeders\\',
+ ];
+
+ $className = $name;
- /**
- * @var Seeder
- */
- $seeder = new $seedClass($this->db);
+ // Si le nom ne contient pas de namespace, on essaie les chemins standards
+ if (!str_contains($name, '\\')) {
+ foreach ($paths as $path) {
+ $fullClass = $path . $name;
+ if (class_exists($fullClass)) {
+ $className = $fullClass;
+ break;
+ }
+ }
+ }
- if ($seeder->getLocale() === '') {
- $seeder->setLocale(config('app.language'));
+ if (!class_exists($className)) {
+ throw new InvalidArgumentException(
+ "Le seeder '{$name}' n'a pas été trouvé.\n" .
+ "Chemins recherchés :\n" .
+ implode("\n", array_map(fn($p) => "- {$p}{$name}", $paths))
+ );
}
- $this->task('Demarrage du seed')->eol();
- sleep(2);
- $this->info('Remplissage en cours de traitement');
+ return new $className($this->db);
+ }
- if (method_exists($seeder, 'run')) {
- Services::container()->call([$seeder, 'run']);
+ /**
+ * Configure le seeder
+ */
+ protected function configureSeeder(Seeder $seeder, ?string $locale, bool $silent): void
+ {
+ if ($locale !== null) {
+ $seeder->setLocale($locale);
+ } elseif ($seeder->getLocale() === '') {
+ $seeder->setLocale(config('app.language', 'fr_FR'));
}
- $usedSeed = [
- Services::container()->call([$seeder, 'execute']),
- ...$seeder->getSeeded(),
+ if ($silent) {
+ $seeder->setSilent(true);
+ }
+ }
+
+ /**
+ * Exécute le seeder
+ */
+ protected function runSeeder(Seeder $seeder): void
+ {
+ $this->task('Démarrage du seed')->eol();
+
+ $this->info('Remplissage en cours...');
+
+ $seeder->setCommand($this)->execute();
+
+ $executed = [
+ $seeder::class,
+ ...$seeder->getCalled(),
];
- $this->eol()->success('Opération terminée.');
+ $this->eol()->success('Opération terminée avec succès !');
- foreach ($usedSeed as $seeded) {
- $this->eol()->write('- ')->writer->yellow($seeded);
- }
+ $this->eol()->write('Seeders exécutés :');
- return EXIT_SUCCESS;
+ foreach (array_unique($executed) as $seeded) {
+ $this->eol()->write(' ✔ ')->writer->green($seeded);
+ }
}
}
diff --git a/src/Config/Services.php b/src/Config/Services.php
index 155a10f..64c41e0 100644
--- a/src/Config/Services.php
+++ b/src/Config/Services.php
@@ -12,6 +12,8 @@
namespace BlitzPHP\Database\Config;
use BlitzPHP\Container\Services as BaseServices;
+use BlitzPHP\Contracts\Database\BuilderInterface;
+use BlitzPHP\Contracts\Database\ConnectionInterface;
use BlitzPHP\Database\Builder\BaseBuilder;
use BlitzPHP\Database\Connection\BaseConnection;
use BlitzPHP\Database\DatabaseManager;
@@ -32,7 +34,7 @@ class Services extends BaseServices
/**
* Récupère le gestionnaire de base de données
*/
- protected static function manager(): DatabaseManager
+ public static function dbManager(): DatabaseManager
{
if (static::$manager === null) {
static::$manager = new DatabaseManager(static::logger(), static::event());
@@ -43,10 +45,12 @@ protected static function manager(): DatabaseManager
/**
* Récupère une connexion à la base de données
+ *
+ * @return BaseConnection
*/
- public static function database(?string $group = null, bool $shared = true): BaseConnection
+ public static function database(?string $group = null, bool $shared = true): ConnectionInterface
{
- $connection = static::manager()->connect($group, $shared);
+ $connection = static::dbManager()->connect($group, $shared);
if (!$connection instanceof BaseConnection) {
throw new InvalidArgumentException('La connexion retournée n\'est pas une instance de BaseConnection');
@@ -57,8 +61,12 @@ public static function database(?string $group = null, bool $shared = true): Bas
/**
* Récupère un query builder
+ *
+ * @return BaseBuilder
+ *
+ * @deprecated 1.0 use static::database()->table($tablename) instead
*/
- public static function builder(?string $group = null, bool $shared = true): BaseBuilder
+ public static function builder(?string $group = null, bool $shared = true): BuilderInterface
{
$key = 'builder_' . ($group ?? 'default');
@@ -66,7 +74,7 @@ public static function builder(?string $group = null, bool $shared = true): Base
return static::$instances[$key];
}
- $builder = static::manager()->builder(static::database($group, $shared));
+ $builder = static::dbManager()->builder(static::database($group, $shared));
if ($shared) {
static::$instances[$key] = $builder;
@@ -78,7 +86,7 @@ public static function builder(?string $group = null, bool $shared = true): Base
/**
* Récupère un exportateur de base de données
*/
- public static function dbExporter(?BaseConnection $db = null, array $config = [], bool $shared = true): Exporter
+ public static function dbExporter(?ConnectionInterface $db = null, array $config = [], bool $shared = true): Exporter
{
if ($shared) {
return static::sharedInstance('dbExporter', $db, $config);
@@ -102,7 +110,7 @@ public static function dbExporter(?BaseConnection $db = null, array $config = []
/**
* Récupère un importateur de base de données
*/
- public static function dbImporter(?BaseConnection $db = null, array $config = [], bool $shared = true): Importer
+ public static function dbImporter(?ConnectionInterface $db = null, array $config = [], bool $shared = true): Importer
{
if ($shared) {
return static::sharedInstance('dbImporter', $db, $config);
diff --git a/src/Exceptions/DatabaseException.php b/src/Exceptions/DatabaseException.php
index 91718d2..ceab403 100644
--- a/src/Exceptions/DatabaseException.php
+++ b/src/Exceptions/DatabaseException.php
@@ -21,4 +21,9 @@ class DatabaseException extends Error implements ExceptionInterface
* @var int
*/
protected $code = 8;
+
+ protected static function t(string $message, array $args = []): string
+ {
+ return sprintf($message, $args);
+ }
}
diff --git a/src/Exceptions/SeederException.php b/src/Exceptions/SeederException.php
new file mode 100644
index 0000000..07fd034
--- /dev/null
+++ b/src/Exceptions/SeederException.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace BlitzPHP\Database\Seeder;
+
+use Faker\Factory as FakerFactory;
+use Faker\Generator as FakerGenerator;
+
+/**
+ * Générateur de configurations pour les seeders
+ *
+ * @mixin FakerGenerator
+ */
+class Factory
+{
+ /**
+ * Instance Faker
+ */
+ protected FakerGenerator $faker;
+
+ /**
+ * Constructeur
+ */
+ public function __construct(string $locale = 'fr_FR')
+ {
+ $this->faker = FakerFactory::create($locale);
+ }
+
+ /**
+ * Appel Faker (retourne une configuration)
+ */
+ public function __call(string $name, array $arguments): array
+ {
+ if ($name === 'unique') {
+ return ['faker:unique', $arguments[0] ?? null, array_slice($arguments, 1) ?? []];
+ }
+
+ return ['faker', $name, $arguments];
+ }
+
+ /**
+ * Propriété Faker (retourne une configuration)
+ */
+ public function __get(string $name): array
+ {
+ return ['faker', $name, []];
+ }
+
+ /**
+ * Relation avec une autre table
+ */
+ public function relation(string $table, string $column = 'id'): array
+ {
+ return ['relation', $table, $column];
+ }
+
+ /**
+ * Valeur optionnelle
+ */
+ public function optional(float $weight = 0.5, mixed $default = null, mixed $value = null): array
+ {
+ if ($value === null) {
+ // Si pas de valeur fournie, on utilisera une closure
+ return ['optional', $weight, $default];
+ }
+ return ['optional', $weight, $default, $value];
+ }
+
+ /**
+ * Valeur fixe (pour compatibilité)
+ */
+ public function raw(mixed $value): mixed
+ {
+ return $value;
+ }
+}
diff --git a/src/Seeder/Faker.php b/src/Seeder/Faker.php
deleted file mode 100644
index da639c9..0000000
--- a/src/Seeder/Faker.php
+++ /dev/null
@@ -1,238 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace BlitzPHP\Database\Seeder;
-
-use DateTime;
-
-/**
- * Generator
- *
- * @credit tebazil/db-seeder
- *
- * @property string $address
- * @property string $amPm
- * @property string $bankAccountNumber
- * @property string $buildingNumber
- * @property int $century
- * @property string $chrome
- * @property string $city
- * @property string $citySuffix
- * @property string $colorName
- * @property string $company
- * @property string $companyEmail
- * @property string $companySuffix
- * @property string $country
- * @property string $countryCode
- * @property string $countryISOAlpha3
- * @property string $creditCardDetails
- * @property DateTime $creditCardExpirationDate
- * @property string $creditCardExpirationDateString
- * @property string $creditCardNumber
- * @property string $creditCardType
- * @property string $currencyCode
- * @property DateTime $dateTime
- * @property DateTime $dateTimeAD
- * @property DateTime $dateTimeThisCentury
- * @property DateTime $dateTimeThisDecade
- * @property DateTime $dateTimeThisMonth
- * @property DateTime $dateTimeThisYear
- * @property int $dayOfMonth
- * @property int $dayOfWeek
- * @property string $domainName
- * @property string $domainWord
- * @property string $ean13
- * @property string $ean8
- * @property string $email
- * @property string $fileExtension
- * @property string $firefox
- * @property string $firstName
- * @property string $firstNameFemale
- * @method array shuffleArray(array $array = array())
- * @property string $firstNameMale
- * @method string asciify($string = '****')
- * @property string $freeEmail
- * @method int biasedNumberBetween($min = 0, $max = 100, $function = 'sqrt')
- * @property string $freeEmailDomain
- * @method bool boolean($chanceOfGettingTrue = 50)
- * @method string bothify($string = '## ??')
- *
- * @property string $hexColor
- * @property string $internetExplorer
- * @property string $ipv4
- * @property string $ipv6
- * @property string $isbn10
- * @property string $isbn13
- * @property string $iso8601
- * @property string $languageCode
- * @method string creditCardNumber($type = null, $formatted = false, $separator = '-')
- * @property string $lastName
- * @property float $latitude
- * @property string $linuxPlatformToken
- * @property string $linuxProcessor
- * @property string $locale
- * @method string date($format = 'Y-m-d', $max = 'now')
- * @property string $localIpv4
- * @property float $longitude
- * @property string $macAddress
- * @property string $macPlatformToken
- * @property string $macProcessor
- * @property string $md5
- * @property string $mimeType
- * @property int $month
- * @property string $monthName
- * @property string $name
- * @property string $opera
- * @property string $paragraph
- * @property array|string $paragraphs
- * @property string $password
- * @property string $phoneNumber
- * @property string $postcode
- * @property string $randomAscii
- * @property int $randomDigit
- * @property int $randomDigitNotNull
- * @property string $randomLetter
- * @method DateTime dateTimeBetween($startDate = '-30 years', $endDate = 'now')
- * @method string file($sourceDirectory = '/tmp', $targetDirectory = '/tmp', $fullPath = true)
- * @method string image($dir = null, $width = 640, $height = 480, $category = null, $fullPath = true)
- *
- * @property string $rgbColor
- * @property array $rgbColorAsArray
- * @property string $rgbCssColor
- * @property string $safari
- * @property string $safeColorName
- * @property string $safeEmail
- * @property string $safeEmailDomain
- * @property string $safeHexColor
- * @method string imageUrl($width = 640, $height = 480, $category = null, $randomize = true)
- *
- * @property string $sentence
- * @property array|string $sentences
- * @property string $sha1
- * @property string $sha256
- * @method string lexify($string = '????')
- * @method int numberBetween($min = 0, $max = 2147483647)
- * @method string numerify($string = '###')
- * @method string paragraph($nbSentences = 3, $variableNbSentences = true)
- * @method array|string paragraphs($nb = 3, $asText = false)
- * @method string password($minLength = 6, $maxLength = 20)
- * @method float randomFloat($nbMaxDecimals = null, $min = 0, $max = null)
- * @method int randomNumber($nbDigits = null, $strict = false)
- * @method string realText($maxNbChars = 200, $indexSize = 2)
- * @method string regexify($regex = '')
- * @method string sentence($nbWords = 6, $variableNbWords = true)
- * @method array|string sentences($nb = 3, $asText = false)
- * @method array|string shuffle($arg = '')
- * @method string shuffleString($string = '', $encoding = 'UTF-8')
- * @method string slug($nbWords = 6, $variableNbWords = true)
- * @method string text($maxNbChars = 200)
- * @method string time($format = 'H:i:s', $max = 'now')
- *
- * @property string $slug
- * @property string $streetAddress
- * @property string $streetName
- * @property string $streetSuffix
- * @property string $swiftBicNumber
- * @property string $text
- * @property string $timezone
- * @property string $title
- * @property string $titleFemale
- * @property string $titleMale
- * @property string $tld
- * @property int $unixTime
- * @property string $url
- * @property string $userAgent
- * @method string toLower($string = '')
- * @method string toUpper($string = '')
- * @method array|string words($nb = 3, $asText = false)
- *
- * @property string $userName
- * @property string $uuid
- * @property string $vat
- * @property string $windowsPlatformToken
- * @property string $word
- * @property array|string $words
- * @property int $year
- */
-class Faker
-{
- public const OPTIONAL = 'optional';
- public const UNIQUE = 'unique';
- public const VALID = 'valid';
- private const DEFAULT_OPTIONS = [
- self::OPTIONAL => false,
- self::UNIQUE => false,
- self::VALID => false,
- ];
-
- private array $options = self::DEFAULT_OPTIONS;
-
- public function __get(string $property)
- {
- return $this->retrive($property);
- }
-
- public function __call(string $method, array $arguments)
- {
- return $this->retrive($method, $arguments);
- }
-
- /**
- * Specifie qu'on souhaite avoir des données optionelles
- */
- public function optional(float $weight = 0.5, mixed $default = null): self
- {
- $this->options[self::OPTIONAL] = func_get_args();
-
- return $this;
- }
-
- /**
- * Specifie qu'on souhaite avoir des donnees uniques
- */
- public function unique(bool $reset = false, int $maxRetries = 10000): self
- {
- $this->options[self::UNIQUE] = func_get_args();
-
- return $this;
- }
-
- /**
- * Specifie qu'on veut les donnees valide
- *
- * @param mixed $validator
- */
- public function valid($validator, int $maxRetries = 10000): self
- {
- $this->options[self::VALID] = func_get_args();
-
- return $this;
- }
-
- /**
- * Facade de recuperation les valeurs de configuration de fakerphp
- */
- private function retrive(string $name, ?array $arguments = null): mixed
- {
- $return = [Generator::FAKER, $name, $arguments, $this->options];
- $this->optionsReset();
-
- return $return;
- }
-
- /**
- * Reinitialise les options du generateur
- */
- private function optionsReset()
- {
- $this->options = self::DEFAULT_OPTIONS;
- }
-}
diff --git a/src/Seeder/Generator.php b/src/Seeder/Generator.php
deleted file mode 100644
index e792988..0000000
--- a/src/Seeder/Generator.php
+++ /dev/null
@@ -1,155 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace BlitzPHP\Database\Seeder;
-
-use Faker\Factory;
-use Faker\Generator as TrueFaker;
-use InvalidArgumentException;
-
-/**
- * Generateur de données
- *
- * @credit tebazil/db-seeder
- */
-class Generator
-{
- public const PK = 'pk';
- public const FAKER = 'faker';
- public const RELATION = 'relation';
-
- private bool $reset = false;
-
- /**
- * Instance du generateur de fake data
- */
- private ?TrueFaker $faker = null;
-
- /**
- * Valeur courante de la cle primaire
- */
- private int $pkValue = 1;
-
- /**
- * Liste des tables pour lesquelles les donnees doivent etre generees
- */
- private array $tables = [];
-
- public function __construct(private string $locale)
- {
- }
-
- /**
- * Recupere une valeur generee
- */
- public function getValue(array|string $config): mixed
- {
- if (! is_array($config)) {
- $config = [$config];
- }
-
- return match ($config[0]) {
- self::PK => $this->pkValue(),
- self::FAKER => $this->fakerValue($config[1], $config[2] ?? [], $config[3] ?? []),
- self::RELATION => $this->relationValue($config[1], $config[2] ?? ''),
- default => is_callable($config[0]) ? $config[0]() : $config[0]
- };
- }
-
- /**
- * Reinitialise les valeurs du generateur
- *
- * @return void
- */
- public function reset()
- {
- $this->reset = true;
- }
-
- /**
- * Definition des champs d'une table
- */
- public function setColumns(string $table, array $columns)
- {
- $this->tables[$table] = $columns;
- }
-
- /**
- * Recupere la cle primaire auto incrementee
- */
- private function pkValue(): int
- {
- if ($this->reset) {
- $this->pkValue = 1;
- $this->reset = false;
- }
-
- return $this->pkValue++;
- }
-
- /**
- * Recupere une valeur generee par Faker
- */
- private function fakerValue(mixed $format, array $arguments, array $options): mixed
- {
- if (empty($format)) {
- return null;
- }
-
- $faker = $this->faker();
-
- if (isset($options[Faker::UNIQUE]) && is_array($options[Faker::UNIQUE])) {
- $faker = call_user_func_array([$faker, 'unique'], $options[Faker::UNIQUE]);
- }
-
- if (isset($options[Faker::OPTIONAL]) && is_array($options[Faker::OPTIONAL])) {
- $faker = call_user_func_array([$faker, 'optinal'], $options[Faker::OPTIONAL]);
- }
-
- if (isset($options[Faker::VALID]) && is_array($options[Faker::VALID])) {
- $faker = call_user_func_array([$faker, 'valid'], $options[Faker::VALID]);
- }
-
- return $faker->format($format, $arguments);
- }
-
- /**
- * Recupere une valeur issue d'une relation
- */
- private function relationValue(string $table, string $column): mixed
- {
- if (! $this->isColumnSet($table, $column)) {
- throw new InvalidArgumentException("Table {$table} , column {$column} is not filled");
- }
-
- return $this->tables[$table][$column][array_rand($this->tables[$table][$column])];
- }
-
- /**
- * Instance unique du generateur faker pour le generateur courant
- */
- private function faker(): TrueFaker
- {
- if (null === $this->faker) {
- $this->faker = Factory::create($this->locale);
- }
-
- return $this->faker;
- }
-
- /**
- * Verifie si un champ de table a une valeur renseignee
- */
- private function isColumnSet(string $table, string $column): bool
- {
- return isset($this->tables[$table]) && isset($this->tables[$table][$column]);
- }
-}
diff --git a/src/Seeder/Seed.php b/src/Seeder/Seed.php
new file mode 100644
index 0000000..6d6e5ba
--- /dev/null
+++ b/src/Seeder/Seed.php
@@ -0,0 +1,456 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace BlitzPHP\Database\Seeder;
+
+use BlitzPHP\Database\Builder\BaseBuilder;
+use BlitzPHP\Database\Connection\BaseConnection;
+use BlitzPHP\Database\Exceptions\SeederException;
+use Faker\Generator as FakerGenerator;
+use PDO;
+
+/**
+ * Définition d'une table à remplir
+ */
+class Seed
+{
+ /**
+ * Taille des lots pour bulk insert
+ */
+ protected const DEFAULT_BULK_SIZE = 100;
+
+ /**
+ * Builder pour la table
+ */
+ protected BaseBuilder $builder;
+
+ /**
+ * Définition des colonnes
+ *
+ * @var array
+ */
+ protected array $columns = [];
+
+ /**
+ * Closures pour les cas complexes
+ *
+ * @var array
+ */
+ protected array $closures = [];
+
+ /**
+ * Données brutes
+ */
+ protected array $rawData = [];
+
+ /**
+ * Nombre de lignes à générer
+ */
+ protected int $rowCount = 30;
+
+ /**
+ * Taille des lots pour bulk insert
+ */
+ protected int $bulkSize = self::DEFAULT_BULK_SIZE;
+
+ /**
+ * Faut-il vider la table avant ?
+ */
+ protected bool $truncate = false;
+
+ /**
+ * Callbacks avant insertion
+ *
+ * @var list
+ */
+ protected array $beforeInsertCallbacks = [];
+
+ /**
+ * Callbacks après insertion
+ *
+ * @var list
+ */
+ protected array $afterInsertCallbacks = [];
+
+ /**
+ * Données générées
+ */
+ protected array $generated = [];
+
+ /**
+ * Cache des valeurs résolues
+ */
+ protected array $cache = [];
+
+ /**
+ * Constructeur
+ *
+ * @param BaseConnection $db Connexion à la base de données
+ * @param string $table Nom de la table
+ * @param FakerGenerator $faker Générateur Faker
+ */
+ public function __construct(protected BaseConnection $db, protected string $table, protected FakerGenerator $faker)
+ {
+ $this->builder = $db->table($table);
+ }
+
+ /**
+ * Définit les colonnes
+ */
+ public function columns(array $columns): self
+ {
+ foreach ($columns as $name => $definition) {
+ $this->column($name, $definition);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Ajoute une colonne
+ */
+ public function column(string $name, mixed $definition): self
+ {
+ // Si c'est une closure, on la garde à part pour la flexibilité
+ if (is_callable($definition) && !is_string($definition)) {
+ $this->closures[$name] = $definition;
+ } else {
+ $this->columns[$name] = $definition;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Définit le nombre de lignes à insérer en base de données
+ */
+ public function rows(int $count): self
+ {
+ $this->rowCount = $count;
+
+ return $this;
+ }
+
+ /**
+ * Définit la taille des lots pour bulk insert
+ */
+ public function bulkSize(int $size): self
+ {
+ $this->bulkSize = max(1, $size);
+
+ return $this;
+ }
+
+ /**
+ * Définit des données brutes
+ */
+ public function data(array $data): self
+ {
+ if ($data === []) {
+ throw SeederException::dataCannotBeEmpty();
+ }
+
+ $firstRow = reset($data);
+
+ if (! is_array($firstRow)) {
+ $this->rawData = [$data];
+ } else {
+ $this->rawData = $data;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Vide la table avant insertion
+ */
+ public function truncate(bool $truncate = true): self
+ {
+ $this->truncate = $truncate;
+
+ return $this;
+ }
+
+ /**
+ * Ajoute un callback avant chaque insertion
+ *
+ * @param Closure(array $data, int $index, ?int $insertId): array|null|void $callback
+ */
+ public function beforeInsert(callable $callback): self
+ {
+ $this->beforeInsertCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Ajoute un callback après chaque insertion
+ *
+ * @param Closure(array $data, int $index, ?int $insertId): array|null|void $callback
+ */
+ public function afterInsert(callable $callback): self
+ {
+ $this->afterInsertCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Exécute le seed
+ */
+ public function execute(): void
+ {
+ if ($this->truncate) {
+ $this->builder->truncate();
+ }
+
+ if ($this->rawData !== []) {
+ $this->insertRawData();
+ } else {
+ $this->generateAndInsertData();
+ }
+ }
+
+ /**
+ * Insère les données brutes
+ */
+ protected function insertRawData(): void
+ {
+ $columns = array_keys(reset($this->rawData));
+
+ $chunks = array_chunk($this->rawData, $this->bulkSize);
+
+ foreach ($chunks as $chunkIndex => $chunk) {
+ $data = [];
+ foreach ($chunk as $rowIndex => $row) {
+ $preparedRow = [];
+ foreach ($columns as $column) {
+ $preparedRow[$column] = $row[$column] ?? null;
+ }
+
+ $this->executeCallbacks($this->beforeInsertCallbacks, $preparedRow, $rowIndex);
+ $data[] = $preparedRow;
+ }
+
+ $this->builder->bulkInsert($data);
+
+ // Callbacks after (avec l'ID du premier élément comme approximation)
+ $firstId = $this->db->lastId();
+ foreach ($data as $index => $row) {
+ $this->executeCallbacks($this->afterInsertCallbacks, $row, $index, $firstId + $index);
+ }
+ }
+ }
+
+ /**
+ * Génère et insère les données
+ */
+ protected function generateAndInsertData(): void
+ {
+ // Résoudre les dépendances une seule fois
+ $dependencies = $this->resolveDependencies();
+
+ // Générer les données par lots
+ $batches = (int) ceil($this->rowCount / $this->bulkSize);
+
+ for ($batch = 0; $batch < $batches; $batch++) {
+ $batchData = [];
+ $start = $batch * $this->bulkSize;
+ $end = min($start + $this->bulkSize, $this->rowCount);
+
+ for ($i = $start; $i < $end; $i++) {
+ $row = [];
+
+ // Traiter les configurations d'abord (plus rapides)
+ foreach ($this->columns as $column => $definition) {
+ $row[$column] = $this->resolveConfig($definition, $dependencies);
+ }
+
+ // Traiter les closures ensuite (plus flexibles)
+ foreach ($this->closures as $column => $closure) {
+ $row[$column] = $closure($this->faker, $dependencies, $i);
+ }
+
+ $this->executeCallbacks($this->beforeInsertCallbacks, $row, $i);
+
+ $batchData[] = $row;
+ }
+
+ $this->builder->bulkInsert($batchData);
+
+ // Callbacks after (avec approximation des IDs)
+ $firstId = $this->db->lastId();
+ foreach ($batchData as $offset => $row) {
+ $this->executeCallbacks($this->afterInsertCallbacks, $row, $start + $offset, $firstId + $offset);
+ }
+ }
+ }
+
+ /**
+ * Résout une configuration
+ */
+ protected function resolveConfig(mixed $config, array $dependencies): mixed
+ {
+ // Cache pour les appels répétés
+ $cacheKey = is_array($config) ? md5(serialize($config)) : null;
+
+ if ($cacheKey && isset($this->cache[$cacheKey])) {
+ return $this->cache[$cacheKey];
+ }
+
+ $result = match (true) {
+ // Valeur simple (pas de configuration)
+ !is_array($config) => $config,
+
+ // Configuration standard
+ default => $this->resolveArrayConfig($config, $dependencies)
+ };
+
+ if ($cacheKey) {
+ $this->cache[$cacheKey] = $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Résout une configuration sous forme de tableau
+ */
+ protected function resolveArrayConfig(array $config, array $dependencies): mixed
+ {
+ $type = $config[0] ?? null;
+
+ return match ($type) {
+ 'faker' => $this->resolveFaker($config),
+ 'faker:unique' => $this->resolveUniqueFaker($config),
+ 'relation' => $this->resolveRelation($config, $dependencies),
+ 'optional' => $this->resolveOptional($config, $dependencies),
+ default => $config // Retourne tel quel si non reconnu
+ };
+ }
+
+ /**
+ * Résout un appel Faker
+ */
+ protected function resolveFaker(array $config): mixed
+ {
+ $method = $config[1] ?? null;
+ $arguments = $config[2] ?? [];
+
+ if (!$method) {
+ throw SeederException::unspecifiedFakerMethod();
+ }
+
+ return $this->faker->{$method}(...$arguments);
+ }
+
+ /**
+ * Résout un appel Faker unique
+ */
+ protected function resolveUniqueFaker(array $config): mixed
+ {
+ $method = $config[1] ?? null;
+ $arguments = $config[2] ?? [];
+ $maxRetries = $config[3] ?? 10000;
+
+ if (!$method) {
+ throw SeederException::unspecifiedFakerMethod();
+ }
+
+ return $this->faker->unique(maxRetries: (int) $maxRetries)->{$method}(...$arguments);
+ }
+
+ /**
+ * Résout une relation
+ */
+ protected function resolveRelation(array $config, array $dependencies): mixed
+ {
+ $table = $config[1] ?? null;
+ $column = $config[2] ?? 'id';
+
+ if (!$table || !isset($dependencies[$table])) {
+ throw SeederException::relationTableNotFound($table);
+ }
+
+ $values = $dependencies[$table];
+ return $values[array_rand($values)];
+ }
+
+ /**
+ * Résout une valeur optionnelle
+ */
+ protected function resolveOptional(array $config, array $dependencies): mixed
+ {
+ $weight = $config[1] ?? 0.5;
+ $default = $config[2] ?? null;
+ $value = $config[3] ?? null;
+
+ if (mt_rand() / mt_getrandmax() <= $weight) {
+ if ($value === null) {
+ // Pas de valeur fournie, on utilise une closure par défaut
+ // ou on retourne null
+ return null;
+ }
+ return $this->resolveConfig($value, $dependencies);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Résout les dépendances entre tables
+ */
+ protected function resolveDependencies(): array
+ {
+ $dependencies = [];
+
+ // Chercher les relations dans les configurations
+ foreach ($this->columns as $definition) {
+ if (is_array($definition) && ($definition[0] ?? null) === 'relation') {
+ $table = $definition[1] ?? null;
+ if ($table && $table !== $this->table) {
+ $dependencies[$table] = $this->getTableValues($table, $definition[2] ?? 'id');
+ }
+ }
+ }
+
+ // TODO Chercher aussi dans les closures (moins probable mais possible)
+ foreach ($this->closures as $closure) {
+ // On ne peut pas analyser les closures statiquement
+ // On laisse l'utilisateur gérer
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ * Récupère les valeurs d'une table pour les relations
+ */
+ protected function getTableValues(string $table, string $column): array
+ {
+ return $this->db->table($table)
+ ->select($column)
+ ->all(PDO::FETCH_COLUMN);
+ }
+
+ /**
+ * Exécute les callbacks
+ */
+ protected function executeCallbacks(array $callbacks, array &$data, int $index, $insertId = null): void
+ {
+ foreach ($callbacks as $callback) {
+ $result = $callback($data, $index, $insertId);
+ if ($result !== null) {
+ $data = $result; // Permet de modifier les données
+ }
+ }
+ }
+}
diff --git a/src/Seeder/Seeder.php b/src/Seeder/Seeder.php
index 6d6ec85..1cbe188 100644
--- a/src/Seeder/Seeder.php
+++ b/src/Seeder/Seeder.php
@@ -11,196 +11,206 @@
namespace BlitzPHP\Database\Seeder;
+use BlitzPHP\Cli\Console\Command;
use BlitzPHP\Database\Connection\BaseConnection;
-use BlitzPHP\Database\Exceptions\DatabaseException;
-use InvalidArgumentException;
+use BlitzPHP\Database\Exceptions\SeederException;
/**
- * Genere du faux contenu pour remplir une base de donnees.
+ * Classe de base pour les seeders
*
- * @credit tebazil/db-seeder
+ * @inspired https://github.com/tebazil/db-seeder
*/
abstract class Seeder
{
/**
- * @var list Liste des tables a remplir
+ * Connexion à la base de données
*/
- private array $tables = [];
+ protected BaseConnection $db;
/**
- * Générateur de contenu
+ * Factory pour les configurations
*/
- private ?Generator $generator = null;
+ protected Factory $factory;
/**
- * Liste des tables qui ont deja été remplies
+ * Instance de la console
*/
- private array $filledTablesNames = [];
+ protected ?Command $command = null;
/**
- * @var list Liste des seeders executes
+ * Tables à remplir
+ *
+ * @var array
*/
- private array $seeded = [];
+ protected array $seeds = [];
/**
- * Langue à utiliser pour la génération des fake data via Faker
+ * Seeders appelés
+ *
+ * @var list
*/
- protected string $locale = '';
+ protected array $called = [];
/**
- * If true, will not display CLI messages.
+ * Mode silencieux (pas de sortie)
*/
protected bool $silent = false;
- public function __construct(protected BaseConnection $db)
+ /**
+ * Langue pour Faker
+ */
+ protected string $locale = 'fr_FR';
+
+ /**
+ * Constructeur
+ *
+ * @param BaseConnection $db Connexion à la base de données
+ */
+ public function __construct(BaseConnection $db)
{
+ $this->db = $db;
+
+ $this->factory = new Factory($this->locale);
}
/**
- * Sets the silent treatment.
+ * Définit l'instance de commande
*/
- public function setSilent(bool $silent): self
+ public function setCommand(?Command $command): self
{
- $this->silent = $silent;
+ $this->command = $command;
return $this;
}
/**
- * Recupere la langue de generation de contenu
+ * Définit le mode silencieux
*/
- public function getLocale(): string
+ public function setSilent(bool $silent): self
{
- return $this->locale;
+ $this->silent = $silent;
+
+ return $this;
}
/**
- * Modifie la langue de generation de contenu
+ * Définit la langue
*/
public function setLocale(string $locale): self
{
$this->locale = $locale;
-
+ $this->factory = new Factory($locale);
+
return $this;
}
/**
- * Recupere la liste des sous seeder executes via la methode call()
- *
- * @return list
+ * Récupère la langue
*/
- public function getSeeded(): array
+ public function getLocale(): string
{
- return $this->seeded;
+ return $this->locale;
}
/**
- * Lance la generation des donnees
+ * Récupère les seeders appelés
+ *
+ * @return list
*/
- public function execute(): string
+ public function getCalled(): array
{
- $this->checkCrossDependentTables();
-
- $tableNames = array_keys($this->tables);
- sort($tableNames);
-
- $foolProofCounter = 0;
- $tableNamesIntersection = [];
-
- while ($tableNamesIntersection !== $tableNames) {
- if ($foolProofCounter++ > 500) {
- throw new DatabaseException("Quelque chose d'inattendu s'est produit\u{a0}: certaines tables ne peuvent peut-être pas être remplies");
- }
+ return $this->called;
+ }
- foreach ($this->tables as $tableName => $table) {
- if (! $table->isFilled() && $table->canBeFilled($this->filledTablesNames)) {
- $table->fill();
- $this->generator->setColumns($tableName, $table->getColumns());
+ /**
+ * Accès à la factory (pour la syntaxe $this->faker->...)
+ */
+ public function __get(string $name): mixed
+ {
+ if ($name === 'faker') {
+ return $this->factory;
+ }
+
+ throw SeederException::propertyNotFound($name);
+ }
- if (! in_array($tableName, $this->filledTablesNames, true)) {
- $this->filledTablesNames[] = $tableName;
- }
- }
- }
+ /**
+ * Méthode principale à implémenter
+ */
+ abstract public function run(): void;
- $tableNamesIntersection = array_intersect($this->filledTablesNames, $tableNames);
- sort($tableNamesIntersection);
+ /**
+ * Définit une table à remplir
+ */
+ protected function table(string $table): Seed
+ {
+ if (!isset($this->seeds[$table])) {
+ $this->seeds[$table] = new Seed($this->db, $table, $this->factory->faker);
}
- return static::class;
+ return $this->seeds[$table];
}
/**
- * Specifie la table a remplir.
+ * Appelle d'autres seeders
*/
- protected function table(string $name, bool $truncate = false): TableDef
+ protected function call(array|string $seeders): self
{
- if (! isset($this->tables[$name])) {
- $this->tables[$name] = new Table($this->generator(), $this->db->table($name), $truncate);
+ foreach ((array) $seeders as $seeder) {
+ $seeder = $this->resolve($seeder);
+
+ $seeder->setSilent($this->silent)
+ ->setCommand($this->command)
+ ->setLocale($this->locale)
+ ->run();
+
+ $this->called[] = $seeder::class;
}
- return new TableDef($this->tables[$name]);
+ return $this;
}
/**
- * Charge le seeder spécifié et l'exécute.
- *
- * @throws InvalidArgumentException
+ * Résout un nom de seeder en instance
*/
- protected function call(array|string $classes)
+ protected function resolve(string $class): self
{
- $classes = (array) $classes;
-
- foreach ($classes as $class) {
- $class = trim($class);
-
- if ($class === '') {
- throw new InvalidArgumentException('Aucun seeder n\'a été spécifié.');
- }
-
- /** @var Seeder $seeder */
- $seeder = new $class($this->db);
- $seeder->setSilent($this->silent);
-
- if (method_exists($seeder, 'run')) {
- call_user_func([$seeder, 'run'], new Faker());
- }
-
- $this->seeded[] = $seeder->execute();
-
- unset($seeder);
+ if (!class_exists($class)) {
+ throw SeederException::seederClassDoesNotExist($class);
}
+
+ return new $class($this->db);
}
/**
- * Singleton pour avoir le générateur
+ * Exécute le seeder
*/
- private function generator(): Generator
+ public function execute(): void
{
- if (null === $this->generator) {
- $this->generator = new Generator($this->locale);
+ $this->run();
+
+ foreach ($this->seeds as $seed) {
+ $seed->execute();
}
- return $this->generator;
+ $this->seeds = [];
}
/**
- * Verifie les dependences entres les tables
+ * Affiche un message si pas en mode silencieux
*/
- private function checkCrossDependentTables()
+ protected function output(string $message, string $type = 'info'): void
{
- $dependencyMap = [];
-
- foreach ($this->tables as $tableName => $table) {
- $dependencyMap[$tableName] = $table->getDependsOn();
+ if ($this->silent) {
+ return;
}
- foreach ($dependencyMap as $tableName => $tableDependencies) {
- foreach ($tableDependencies as $dependencyTableName) {
- if (in_array($tableName, $dependencyMap[$dependencyTableName], true)) {
- throw new InvalidArgumentException('Vous ne pouvez pas passer des tables qui dépendent les unes des autres');
- }
- }
+ if ($this->command) {
+ $this->command->{$type}($message);
+ } else if(defined('STDOUT')) {
+ fwrite(STDOUT, $message . PHP_EOL);
+ } else {
+ echo $message . PHP_EOL;
}
}
}
diff --git a/src/Seeder/Table.php b/src/Seeder/Table.php
deleted file mode 100644
index bce1c3f..0000000
--- a/src/Seeder/Table.php
+++ /dev/null
@@ -1,300 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace BlitzPHP\Database\Seeder;
-
-use BlitzPHP\Database\Builder\BaseBuilder;
-use InvalidArgumentException;
-
-/**
- * @credit tebazil/db-seeder
- */
-class Table
-{
- public const DEFAULT_ROW_QUANTITY = 30;
-
- /**
- * Nom de la table courrante
- */
- private string $name;
-
- /**
- * Nom des champs dans lesquels les donnees seront inserees
- */
- private array $columns = [];
-
- /**
- * Quantite de donnees a remplir lors de l'execution
- */
- private ?int $rowQuantity = null;
-
- /**
- * Donnees a remplir lors de l'execution
- */
- private array $rows = [];
-
- /**
- * Donnees brutes a remplir (specifiees par l'utilisateur)
- */
- private array $rawData = [];
-
- /**
- * Drapeau specifiant que la table a ete remplie
- */
- private bool $isFilled = false;
-
- /**
- * Drapeau specifiant que la table a ete partiellement remplie
- */
- private bool $isPartiallyFilled = false;
-
- /**
- * Liste des tables dont depend cette table
- */
- private array $dependsOn = [];
-
- private array $selfDependentColumns = [];
- private array $columnConfig = [];
-
- /**
- * constructor
- */
- public function __construct(private Generator $generator, private BaseBuilder $builder, private bool $truncateTable)
- {
- $this->name = $builder->getTable();
- }
-
- /**
- * Definit les colonnes où les insertions se passeront
- */
- public function setColumns(array $columns): self
- {
- $columnNames = array_keys($columns);
-
- foreach ($columnNames as $columnName) {
- $this->columns[$columnName] = [];
- }
-
- $this->columnConfig = $columns;
-
- $this->calcDependsOn();
- $this->calcSelfDependentColumns();
-
- return $this;
- }
-
- /**
- * Definit le nombre d'element a generer
- */
- public function setRowQuantity(int $rows = self::DEFAULT_ROW_QUANTITY): self
- {
- $this->rowQuantity = $rows;
-
- return $this;
- }
-
- /**
- * Defini les donnees brutes a inserer
- */
- public function setRawData(array $rawData, array $columnNames = []): self
- {
- if ($rawData === []) {
- throw new InvalidArgumentException('$rawData cannot be empty array');
- }
- if (! is_array($firstRow = reset($rawData))) {
- throw new InvalidArgumentException('$rawData should be an array of arrays (2d array)');
- }
- if (is_numeric(key($firstRow)) && ! $columnNames) {
- throw new InvalidArgumentException('Either provide $rawData line arrays with corresponding column name keys, or provide column names in $columnNames');
- }
-
- $this->rawData = $rawData;
- $columnNames = $columnNames ?: array_keys(reset($this->rawData));
- $this->columnConfig = []; // just in case
-
- foreach ($columnNames as $columnName) {
- if ($columnName) {
- $this->columns[$columnName] = []; // we skip false columns and empty columns
- }
-
- $this->columnConfig[] = $columnName;
- }
-
- return $this;
- }
-
- /**
- * Remplissage des donnees
- */
- public function fill(bool $writeDatabase = true)
- {
- [] === $this->rawData
- ? $this->fillFromGenerators($this->columnConfig)
- : $this->fillFromRawData($this->columnConfig, $this->rawData);
-
- if ($this->selfDependentColumns) {
- if ($this->isPartiallyFilled) {
- $this->isFilled = true; // second run
- } else {
- $this->isPartiallyFilled = true; // first run
- }
- } else {
- $this->isFilled = true; // no self-dependent columns
- }
-
- if ($this->isFilled && $writeDatabase) {
- $this->insertData();
- }
- }
-
- /**
- * Véerifie si la table a déjà étée chargée
- */
- public function isFilled(): bool
- {
- return $this->isFilled;
- }
-
- /**
- * Verifie si la table peut etre chargee
- */
- public function canBeFilled(array $filledTableNames): bool
- {
- $intersection = array_intersect($filledTableNames, $this->dependsOn);
- sort($intersection);
-
- return $intersection === $this->dependsOn;
- }
-
- /**
- * Recuperes les valeurs des champs a inserer dans la table
- */
- public function getRows(): array
- {
- return $this->rows;
- }
-
- /**
- * Liste des colonnes de la table dans lesquelles on fera les insertions
- */
- public function getColumns(): array
- {
- return $this->columns;
- }
-
- /**
- * Liste des tables dont depend cette table
- */
- public function getDependsOn(): array
- {
- return $this->dependsOn;
- }
-
- /**
- * Ajoute les donnees de generation a partir des donnees brutes renseignees par l'utilisateur.
- */
- private function fillFromRawData(array $columnConfig, array $data)
- {
- $sizeofColumns = count($columnConfig);
- $data = array_values($data);
- $sizeofData = count($data);
-
- for ($rowNo = 0; $rowNo < ($this->rowQuantity ?? $sizeofData); $rowNo++) {
- $dataKey = ($rowNo < $sizeofData) ? $rowNo : ($rowNo % $sizeofData);
- $rowData = array_values($data[$dataKey]);
-
- for ($i = 0; $i < $sizeofColumns; $i++) {
- if (! $columnConfig[$i]) {
- continue;
- }
-
- $this->rows[$rowNo][$columnConfig[$i]] = $rowData[$i];
- $this->columns[$columnConfig[$i]][$rowNo] = $rowData[$i];
- }
- }
- }
-
- /**
- * Ajoute les donnees de generation a partir du generateur (via Faker si necessaire).
- */
- private function fillFromGenerators(array $columnConfig)
- {
- $this->generator->reset();
-
- for ($rowNo = 0; $rowNo < $this->rowQuantity ?? self::DEFAULT_ROW_QUANTITY; $rowNo++) {
- foreach ($columnConfig as $column => $config) {
- // first and second run separation
- if ($this->selfDependentColumns) {
- $columnIsSelfDependent = in_array($column, $this->selfDependentColumns, true);
- if (! $this->isPartiallyFilled) {
- if ($columnIsSelfDependent) {
- continue;
- }
- } elseif (! $columnIsSelfDependent) {
- continue;
- }
- }
-
- $value = $this->generator->getValue($config);
-
- $this->rows[$rowNo][$column] = $value;
- $this->columns[$column][$rowNo] = $value;
- }
- }
- }
-
- private function calcDependsOn()
- {
- if ($this->rawData) {
- return false;
- }
-
- foreach ($this->columnConfig as $name => $config) {
- if (! is_callable($config)) {
- if (is_array($config) && ($config[0] === Generator::RELATION) && ($this->name !== $config[1])) {
- $this->dependsOn[] = $config[1];
- }
- }
- }
-
- sort($this->dependsOn);
- }
-
- private function calcSelfDependentColumns()
- {
- if ($this->rawData) {
- return false;
- }
-
- foreach ($this->columnConfig as $name => $config) {
- if (! is_callable($config)) {
- if (is_array($config) && ($config[0] === Generator::RELATION) && ($config[1] === $this->name)) {
- $this->selfDependentColumns[] = $name;
- }
- }
- }
- }
-
- /**
- * Insertion des donnees generees en db
- */
- private function insertData()
- {
- if (true === $this->truncateTable) {
- $this->builder->db()->disableFk();
- $this->builder->truncate($this->name);
- }
-
- foreach ($this->rows as $row) {
- $this->builder->into($this->name)->insert($row);
- }
- }
-}
diff --git a/src/Seeder/TableDef.php b/src/Seeder/TableDef.php
deleted file mode 100644
index 643b695..0000000
--- a/src/Seeder/TableDef.php
+++ /dev/null
@@ -1,101 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace BlitzPHP\Database\Seeder;
-
-use BlitzPHP\Utilities\Iterable\Arr;
-use Exception;
-use InvalidArgumentException;
-
-/**
- * @credit tebazil/db-seeder
- */
-class TableDef
-{
- public function __construct(private Table $table)
- {
- }
-
- /**
- * Defini les type de donnees a generer pour chaque colone qu'on souhaite remplir dans la base de donnees
- */
- public function columns(array $columns): self
- {
- $columns = $this->preprocess($columns);
- $this->table->setColumns($columns);
-
- return $this;
- }
-
- /**
- * Specifie le nombre de ligne a inserer dans la table
- */
- public function rows(int $rows = Table::DEFAULT_ROW_QUANTITY): self
- {
- $this->table->setRowQuantity($rows);
-
- return $this;
- }
-
- /**
- * Definit les donnees brutes a inserer
- */
- public function data(array $data): self
- {
- $dim = Arr::dimensions($data);
-
- if ($dim > 2) {
- throw new InvalidArgumentException('Vous ne pouvez pas inserer un tableau de dimension supperieure a 2');
- }
-
- if ($dim === 1) {
- $columnNames = array_keys($data);
- $data = [array_values($data)];
- } else {
- $columnNames = array_keys(reset($data));
- $data = array_map('array_values', $data);
- }
-
- $this->table->setRawData($data, $columnNames);
-
- return $this;
- }
-
- /**
- * Undocumented function
- */
- private function preprocess(array $columns): array
- {
- $newColumns = [];
-
- foreach ($columns as $key => $value) {
- if (is_numeric($key)) {
- if (! is_scalar($value)) {
- throw new Exception("Si la colonne est configurée à la volée, sa valeur doit être scalaire - soit id, soit clé étrangère, c'est-à-dire status_id");
- }
-
- $config = explode('_', $value);
-
- if ($config[0] === 'id') {
- $newColumns[$value] = [Generator::PK];
- } elseif (count($config) === 2 || $config[1] === 'id') {
- $newColumns[$value] = [Generator::RELATION, $config[0], 'id'];
- } else {
- throw new Exception('Le champ ' . $value . ' est mal configuré');
- }
- } else {
- $newColumns[$key] = $value;
- }
- }
-
- return $newColumns;
- }
-}