diff --git a/src/Classes/CaseStyle.php b/src/Classes/CaseStyle.php new file mode 100644 index 0000000..0a5a5af --- /dev/null +++ b/src/Classes/CaseStyle.php @@ -0,0 +1,63 @@ +getConstants()) + ->map(fn ($constant) => $constant->getValue()) + ->toArray(); + } +} diff --git a/src/Exceptions/InvalidCaseStyleException.php b/src/Exceptions/InvalidCaseStyleException.php new file mode 100644 index 0000000..c0e959d --- /dev/null +++ b/src/Exceptions/InvalidCaseStyleException.php @@ -0,0 +1,14 @@ +message = 'That\'s not a valid case style!'; + } +} diff --git a/src/TypeScriptifyModel.php b/src/TypeScriptifyModel.php index 09bbcbf..a1ff743 100644 --- a/src/TypeScriptifyModel.php +++ b/src/TypeScriptifyModel.php @@ -3,8 +3,10 @@ namespace SalemC\TypeScriptifyLaravelModels; use SalemC\TypeScriptifyLaravelModels\Exceptions\UnsupportedDatabaseConnection; +use SalemC\TypeScriptifyLaravelModels\Exceptions\InvalidCaseStyleException; use SalemC\TypeScriptifyLaravelModels\Exceptions\InvalidModelException; use SalemC\TypeScriptifyLaravelModels\Utilities\ModelCollector; +use SalemC\TypeScriptifyLaravelModels\Classes\CaseStyle; use Illuminate\Database\Eloquent\Casts\AsStringable; use Illuminate\Database\Eloquent\Model; @@ -50,6 +52,13 @@ final class TypeScriptifyModel { */ private readonly Collection $modelForeignKeyConstraints; + /** + * The selected case style. + * + * @var string + */ + private string $caseStyle = CaseStyle::DEFAULT; + /** * Whether to include the model's $hidden properties. * @@ -68,6 +77,9 @@ final class TypeScriptifyModel { * @param string $fullyQualifiedModelName The fully qualified model class name. * @param ?array $convertedModelsMap The map of `fully qualified model name => interface name` definitions this class can use instead of generating its own definitions. * @param ?string $modelCollectorPath The path to be used for scanning for models. + * + * @throws InvalidModelException + * @throws UnsupportedDatabaseConnection */ public function __construct( private string $fullyQualifiedModelName, @@ -348,6 +360,25 @@ private function convertForeignKeyToPredictedRelationName(string $attribute): st return Str::of($attribute)->replaceLast('_id', '')->camel(); } + /** + * Format `$attribute` to the preferred case style. + * + * @param string $attribute + * + * @return string + */ + private function formatAttributeName(string $attribute): string { + return match ($this->caseStyle) { + CaseStyle::DEFAULT => $attribute, + CaseStyle::CAMEL => Str::camel($attribute), + CaseStyle::PASCAL => Str::studly($attribute), + CaseStyle::SNAKE => Str::snake(Str::studly($attribute)), + // 'kebab' style attributes are supported in TypeScript interfaces + // as long as they're inside quotes. + CaseStyle::KEBAB => sprintf("'%s'", Str::kebab(Str::studly($attribute))), + }; + } + /** * Set whether we should include the model's protected $hidden attributes. * @@ -374,6 +405,25 @@ public function includeRelations(bool $includeRelations): self { return $this; } + /** + * Prefer the `$style` case style. + * + * @param string $caseStyle + * + * @return self + * + * @throws InvalidCaseStyleException + */ + public function preferCaseStyle(string $caseStyle): self { + if (!in_array($caseStyle, CaseStyle::all())) { + throw new InvalidCaseStyleException; + } + + $this->caseStyle = $caseStyle; + + return $this; + } + /** * Generate the TypeScript interface. * @@ -429,7 +479,7 @@ public function generate(): string { } // Append the relation to the interface we're generating. - $outputBuffer->push(sprintf(' %s: %s;', $relationName, $generatedType)); + $outputBuffer->push(sprintf(' %s: %s;', $this->formatAttributeName($relationName), $generatedType)); // If we've generated a new interface, we'll want to append it above the current // interface we're in the process of generating. @@ -451,7 +501,7 @@ public function generate(): string { } } else { // Append the column name, and the TypeScript type to the interface we're generating. - $outputBuffer->push(sprintf(' %s: %s;', $columnSchema->Field, $this->getTypeScriptType($columnSchema))); + $outputBuffer->push(sprintf(' %s: %s;', $this->formatAttributeName($columnSchema->Field), $this->getTypeScriptType($columnSchema))); } });