Skip to content

Instantly share code, notes, and snippets.

@slischka
Created April 26, 2020 17:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slischka/d7df47e7e224d539dbdc8a6f8de3e92e to your computer and use it in GitHub Desktop.
Save slischka/d7df47e7e224d539dbdc8a6f8de3e92e to your computer and use it in GitHub Desktop.
Generator
<?php declare(strict_types = 1);
namespace Fapi\Utils\CodeGenerators\EntityGenerator;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\Dumper;
use Nette\PhpGenerator\Literal;
use Nette\PhpGenerator\Method;
use Nette\Utils\Strings;
use SmartEmailing\Types\PrimitiveTypes;
use function in_array;
use function str_replace;
use function ucwords;
final class Column
{
/** @var string[] */
private array $dbTableTypeToPhp = [
'tinyint' => 'bool',
'int' => 'int',
'decimal' => 'float',
'varchar' => 'string',
'enum' => 'string',
'mediumtext' => 'string',
'text' => 'string',
'datetime' => 'DateTimeImmutable',
'date' => 'DateTimeImmutable',
'json' => 'JsonString',
];
private string $type;
private string $name;
private ?string $default;
private bool $nullable;
private string $phpType;
private Dumper $dumper;
/**
* @param mixed[] $data
*/
public function __construct(array $data)
{
$this->type = PrimitiveTypes::extractString($data, 'Type');
$this->name = PrimitiveTypes::extractString($data, 'Field');
$this->default = PrimitiveTypes::extractStringOrNull($data, 'Default');
$this->nullable = PrimitiveTypes::extractString($data, 'Null') === 'YES';
$this->dumper = new Dumper();
}
public function getPhpType(): string
{
if (!isset($this->phpType)) {
foreach ($this->dbTableTypeToPhp as $dtType => $phpType) {
if (!Strings::contains($this->type, $dtType)) {
continue;
}
$this->phpType = $phpType;
break;
}
}
return $this->phpType;
}
public function getPropName(): string
{
return $this->toPropertyName($this->name);
}
public function getName(): string
{
return $this->name;
}
/**
* @return mixed|null
*/
public function getDefaultPhpValue()
{
if ($this->getPhpType() === 'int') {
return PrimitiveTypes::getIntOrNull($this->default);
}
if ($this->getPhpType() === 'string') {
return PrimitiveTypes::getStringOrNull($this->default);
}
if ($this->getPhpType() === 'float') {
return PrimitiveTypes::getFloatOrNull($this->default);
}
if ($this->getPhpType() === 'bool') {
return PrimitiveTypes::getBoolOrNull($this->default);
}
return null;
}
public function isNullable(): bool
{
return $this->nullable;
}
public function isPrimitive(): bool
{
return in_array($this->getPhpType(), ['bool', 'int', 'float', 'string'], true);
}
public function isDateTime(): bool
{
return $this->getPhpType() === 'DateTimeImmutable';
}
public function isJson(): bool
{
return $this->getPhpType() === 'JsonString';
}
public function addPropertyDefinition(ClassType $classType): void
{
$classType->addProperty($this->getPropName())
->setNullable($this->isNullable() && $this->getDefaultPhpValue() === null)
->setType($this->getPhpType());
}
public function addMethodInicialization(Method $construct): void
{
$statement = '$this->? = ';
$params = [$this->getPropName()];
if ($this->isPrimitive()) {
$statement .= 'PrimitiveTypes::extract?';
$params[] = new Literal(Strings::firstUpper($this->getPhpType()));
if ($this->isNullable() || $this->getDefaultPhpValue() !== null) {
$statement .= '?';
$params[] = new Literal('OrNull');
}
$statement .= '($data, ?)';
$params[] = $this->getName();
if ($this->getDefaultPhpValue() !== null) {
$statement .= ' \?\? ?';
$params[] = $this->getDefaultPhpValue();
}
} else if ($this->isDateTime()) {
if (Strings::contains($this->type, 'datetime')) {
$statement .= 'DateTimesImmutable::extract';
} else {
$statement .= 'DatesImmutable::extract';
}
if ($this->isNullable()) {
$statement .= '?';
$params[] = new Literal('OrNull');
}
$statement .= '($data, ?)';
$params[] = $this->getName();
} elseif ($this->isJson()) {
$statement .= 'JsonString::extract';
if ($this->isNullable() || $this->getDefaultPhpValue() !== null) {
$statement .= '?';
$params[] = new Literal('OrNull');
}
$statement .= '($data, ?)';
$params[] = $this->getName();
if ($this->getDefaultPhpValue() !== null) {
$statement .= ' \?\? JsonString::from(?)';
$params[] = $this->getDefaultPhpValue();
}
}
$statement .= ';';
$construct->addBody($this->dumper->format(
$statement,
...$params
));
}
public function addToArray(Method $toArray): void
{
$statement = ' ? => ';
$params = [$this->getName()];
if ($this->isJson()) {
if ($this->isNullable()) {
$statement .= '$this->? === null \? null : ';
$params[] = $this->getPropName();
}
$statement .= '$this->?->getDecodedValue()';
$params[] = $this->getPropName();
} elseif ($this->isDateTime()) {
if (Strings::contains($this->type, 'datetime')) {
$statement .= 'DateTimeHelpers::formatDateTime';
} else {
$statement .= 'DateTimeHelpers::formatDate';
}
if ($this->isNullable()) {
$statement .= '?';
$params[] = new Literal('OrNull');
}
$statement .= '($this->?)';
$params[] = $this->getPropName();
} else {
$statement .= '$this->?';
$params[] = $this->getPropName();
}
$statement .= ',';
$toArray->addBody($this->dumper->format($statement, ...$params));
}
private function toPascalCase(string $tableName): string
{
return str_replace('_', '', ucwords($tableName, '_'));
}
private function toPropertyName(string $name): string
{
return Strings::firstLower($this->toPascalCase($name));
}
}
<?php declare(strict_types = 1);
namespace Fapi\Utils\CodeGenerators\EntityGenerator;
use DateTimeImmutable;
use Fapi\DateTime\DateTimeHelpers;
use Fapi\Entity\Entity;
use Fapi\Entity\IdIdentifierTrait;
use Fapi\Entity\UserIdTrait;
use Fapi\User\UserIdProvider;
use Nette\Database\Context;
use Nette\Database\SqlLiteral;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\Printer;
use SmartEmailing\Types\DatesImmutable;
use SmartEmailing\Types\DateTimesImmutable;
use SmartEmailing\Types\JsonString;
use SmartEmailing\Types\PrimitiveTypes;
use function iterator_to_array;
use function rtrim;
final class Generator
{
private Context $context;
public function __construct(Context $context)
{
$this->context = $context;
$this->printer = new Printer();
}
public function createEntity(string $tableName): void
{
$className = rtrim($this->toPascalCase($tableName), 's');
$file = new PhpFile();
$file->setStrictTypes();
$class = $file->addClass($className);
$resultSet = $this->context->query('DESCRIBE ?;', new SqlLiteral($tableName));
$columns = [];
foreach ($resultSet->fetchAll() as $prop) {
$columns[] = new Column(iterator_to_array($prop));
}
$file->addUse(Entity::class);
$file->addUse(IdIdentifierTrait::class);
$file->addUse(UserIdProvider::class);
$file->addUse(PrimitiveTypes::class);
$file->addUse(UserIdTrait::class);
$file->addUse(JsonString::class);
$file->addUse(DateTimeImmutable::class);
$file->addUse(DateTimeHelpers::class);
$file->addUse(DatesImmutable::class);
$file->addUse(DateTimesImmutable::class);
$class
->setFinal()
->addImplement(Entity::class)
->addTrait(IdIdentifierTrait::class);
$construct = $class
->addMethod('__construct')
->addComment('@param mixed[] $data');
$construct
->addParameter('data')
->setType('array');
$construct->addBody('$this->pickId($data);');
$toArray = $class->addMethod('toArray')
->setReturnType('array')
->addComment('@inheritdoc');
$toArray->addBody('return [');
$toArray->setReturnType('array');
foreach ($columns as $column) {
if ($column->getName() === 'id') {
$column->addToArray($toArray);
continue;
}
if ($column->getName() === 'user_id') {
$construct->addBody('$this->pickUserId($data);');
$class->addTrait(UserIdTrait::class);
$class->addImplement(UserIdProvider::class);
$column->addToArray($toArray);
continue;
}
$column->addPropertyDefinition($class);
$column->addMethodInicialization($construct);
$column->addToArray($toArray);
}
$toArray->addBody('];');
echo $this->printer->printFile($file);
}
protected function toPascalCase(string $tableName): string
{
return str_replace('_', '', ucwords($tableName, '_'));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment