From a9dddb9e49fd8617cf46450f74716fcf213cfb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 21 Oct 2023 02:54:20 +0000 Subject: [PATCH] Rebase --- .github/workflows/back-end.yml | 5 +- phpstan.neon.dist | 22 ++++- src/Fields/BelongsTo.php | 5 + src/Fields/BelongsToMany.php | 5 + src/Fields/HasMany.php | 3 + src/Fields/HasOne.php | 3 + src/Fields/HasOneOrMany.php | 5 + src/Fields/Meta.php | 3 + src/Fields/MorphMany.php | 3 + src/Fields/MorphOne.php | 5 + src/Fields/MorphOneOrMany.php | 5 + src/Fields/MorphTo.php | 3 + src/Fields/MorphToMany.php | 3 + src/Fields/Relation.php | 5 + src/Models/Medium.php | 2 + src/Navigation/Item.php | 6 ++ src/Support/Slug.php | 170 +++++++++++++++++++++++++++++++++ src/Widgets/Widgets.php | 3 + 18 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 src/Support/Slug.php diff --git a/.github/workflows/back-end.yml b/.github/workflows/back-end.yml index bbf0080db..e3f432763 100644 --- a/.github/workflows/back-end.yml +++ b/.github/workflows/back-end.yml @@ -135,9 +135,12 @@ jobs: name: "Search for $this->$this typo 🐌" run: | ! git grep --line-number -e '\$this\s*->\s*\$this' -- ':!:*/back-end\.yml' + - + name: "Install orchestra/testbench" + run: "composer require --dev orchestra/testbench" - name: "Perform static analysis" - run: "true TODO || composer exec -- phpstan analyze --level=5 src/" + run: "composer exec -- phpstan || true 'Annotate only'" coding_standards: name: "4️⃣ Coding Standards" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 96ad334f8..9312cf752 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,8 +7,28 @@ parameters: paths: - src/ - config/ - - database/ +# TODO +# - database/ - routes/ level: 5 + checkAlwaysTrueCheckTypeFunctionCall: true + checkAlwaysTrueInstanceof: true + checkAlwaysTrueStrictComparison: true + checkAlwaysTrueLooseComparison: true + checkClassCaseSensitivity: false + checkDynamicProperties: true + checkExplicitMixed: false + checkImplicitMixed: false + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true ignoreErrors: - '#^Unsafe usage of new static#' + # --- TODO-s --- + # Tricky readonlys + - '#Assign it in the constructor\.$#' + - '#is assigned outside of the constructor\.$#' + # X vs. X contract + - '#but returns Illuminate\\Contracts\\#' + # SoftDeletes + - '#(\$forceDeleting|::withTrashed|::onlyTrashed|::trashed|::restore)#' diff --git a/src/Fields/BelongsTo.php b/src/Fields/BelongsTo.php index 4fd871e0e..c247595eb 100644 --- a/src/Fields/BelongsTo.php +++ b/src/Fields/BelongsTo.php @@ -6,6 +6,11 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo as EloquentRelation; use Illuminate\Http\Request; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\BelongsTo + * + * @extends \Cone\Root\Fields\Relation + */ class BelongsTo extends Relation { /** diff --git a/src/Fields/BelongsToMany.php b/src/Fields/BelongsToMany.php index a87749eb6..a3fbf2e27 100644 --- a/src/Fields/BelongsToMany.php +++ b/src/Fields/BelongsToMany.php @@ -9,6 +9,11 @@ use Illuminate\Http\Request; use Illuminate\Support\Arr; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\BelongsToMany + * + * @extends \Cone\Root\Fields\Relation + */ class BelongsToMany extends Relation { use ResolvesFields; diff --git a/src/Fields/HasMany.php b/src/Fields/HasMany.php index 1ff452803..8ac39e6a3 100644 --- a/src/Fields/HasMany.php +++ b/src/Fields/HasMany.php @@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany as EloquentRelation; +/** + * @extends \Cone\Root\Fields\HasOneOrMany<\Illuminate\Database\Eloquent\Relations\HasMany> + */ class HasMany extends HasOneOrMany { /** diff --git a/src/Fields/HasOne.php b/src/Fields/HasOne.php index cd071702f..e6bda9b29 100644 --- a/src/Fields/HasOne.php +++ b/src/Fields/HasOne.php @@ -5,6 +5,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne as EloquentRelation; +/** + * @extends \Cone\Root\Fields\HasOneOrMany<\Illuminate\Database\Eloquent\Relations\HasOne> + */ class HasOne extends HasOneOrMany { /** diff --git a/src/Fields/HasOneOrMany.php b/src/Fields/HasOneOrMany.php index 01a6c894a..f58d318b1 100644 --- a/src/Fields/HasOneOrMany.php +++ b/src/Fields/HasOneOrMany.php @@ -7,6 +7,11 @@ use Illuminate\Http\Request; use Illuminate\Support\Arr; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\HasOneOrMany + * + * @extends \Cone\Root\Fields\Relation + */ abstract class HasOneOrMany extends Relation { /** diff --git a/src/Fields/Meta.php b/src/Fields/Meta.php index 37e72eecf..83339a9a4 100644 --- a/src/Fields/Meta.php +++ b/src/Fields/Meta.php @@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Relations\MorphOne as EloquentRelation; use Illuminate\Http\Request; +/** + * @extends \Cone\Root\Fields\MorphOne<\Illuminate\Database\Eloquent\Relations\MorphOne> + */ class Meta extends MorphOne { /** diff --git a/src/Fields/MorphMany.php b/src/Fields/MorphMany.php index 2fa0385e2..7da69d4e3 100644 --- a/src/Fields/MorphMany.php +++ b/src/Fields/MorphMany.php @@ -5,6 +5,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphMany as EloquentRelation; +/** + * @extends \Cone\Root\Fields\MorphOneOrMany<\Illuminate\Database\Eloquent\Relations\MorphMany> + */ class MorphMany extends MorphOneOrMany { /** diff --git a/src/Fields/MorphOne.php b/src/Fields/MorphOne.php index d17f7ace5..e2776f772 100644 --- a/src/Fields/MorphOne.php +++ b/src/Fields/MorphOne.php @@ -5,6 +5,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphOne as EloquentRelation; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\MorphOne + * + * @extends \Cone\Root\Fields\MorphOneOrMany + */ class MorphOne extends MorphOneOrMany { /** diff --git a/src/Fields/MorphOneOrMany.php b/src/Fields/MorphOneOrMany.php index ab928ed59..9ad8d3c48 100644 --- a/src/Fields/MorphOneOrMany.php +++ b/src/Fields/MorphOneOrMany.php @@ -5,6 +5,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphOneOrMany as EloquentRelation; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\MorphOneOrMany + * + * @extends \Cone\Root\Fields\HasOneOrMany + */ abstract class MorphOneOrMany extends HasOneOrMany { /** diff --git a/src/Fields/MorphTo.php b/src/Fields/MorphTo.php index 36a155e6d..afb094bb9 100644 --- a/src/Fields/MorphTo.php +++ b/src/Fields/MorphTo.php @@ -5,6 +5,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo as EloquentRelation; +/** + * @extends \Cone\Root\Fields\BelongsTo<\Illuminate\Database\Eloquent\Relations\MorphTo> + */ class MorphTo extends BelongsTo { /** diff --git a/src/Fields/MorphToMany.php b/src/Fields/MorphToMany.php index 8e3581438..5c98ffa25 100644 --- a/src/Fields/MorphToMany.php +++ b/src/Fields/MorphToMany.php @@ -5,6 +5,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphToMany as EloquentRelation; +/** + * @extends \Cone\Root\Fields\BelongsToMany<\Illuminate\Database\Eloquent\Relations\MorphToMany> + */ class MorphToMany extends BelongsToMany { /** diff --git a/src/Fields/Relation.php b/src/Fields/Relation.php index 3bea00693..fdd80a23a 100644 --- a/src/Fields/Relation.php +++ b/src/Fields/Relation.php @@ -10,6 +10,9 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; +/** + * @template TRelation of \Illuminate\Database\Eloquent\Relations\Relation + */ abstract class Relation extends Field { /** @@ -72,6 +75,8 @@ public static function scopeQuery(Closure $callback): void /** * Get the relation instance. + * + * @phpstan-return TRelation */ public function getRelation(Model $model): EloquentRelation { diff --git a/src/Models/Medium.php b/src/Models/Medium.php index f1f392db2..681b04ecb 100644 --- a/src/Models/Medium.php +++ b/src/Models/Medium.php @@ -152,6 +152,8 @@ public function user(): BelongsTo /** * Determine if the file is image. + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function isImage(): Attribute { diff --git a/src/Navigation/Item.php b/src/Navigation/Item.php index 76e68d8ab..3d83c9450 100644 --- a/src/Navigation/Item.php +++ b/src/Navigation/Item.php @@ -6,6 +6,12 @@ use Cone\Root\Traits\Makeable; use Illuminate\Support\Facades\URL; +/** + * @property string $url + * @property string $label + * @property string $icon + * @property string $group + */ class Item { use HasAttributes; diff --git a/src/Support/Slug.php b/src/Support/Slug.php new file mode 100644 index 000000000..7e9822ba3 --- /dev/null +++ b/src/Support/Slug.php @@ -0,0 +1,170 @@ +model = $model; + } + + /** + * Set the "from" property. + */ + public function from(array|string $attributes): static + { + $this->from = (array) $attributes; + + return $this; + } + + /** + * Set the "to" property + */ + public function to(string $attribute): static + { + $this->to = $attribute; + + return $this; + } + + /** + * Set the "unique" property. + */ + public function unique(bool $value = true): static + { + $this->unique = $value; + + return $this; + } + + /** + * Set the "fresh" property. + */ + public function fresh(bool $value = true): static + { + $this->fresh = $value; + + return $this; + } + + /** + * Set the "resolver" property. + */ + public function generateUsing(Closure $callback): static + { + $this->resolver = $callback; + + return $this; + } + + /** + * Generate the slug. + */ + public function generate(): string + { + $this->from ??= ['id']; + $this->to ??= 'slug'; + $this->separator ??= '-'; + $this->unique ??= false; + $this->fresh ??= false; + + if (! is_null($this->model->getAttribute($this->to)) && ! $this->fresh) { + return $this->model->getAttribute($this->to); + } + + $value = Str::of(implode($this->separator, $this->model->only($this->from))) + ->slug($this->separator) + ->value(); + + if (! is_null($this->resolver)) { + return call_user_func_array($this->resolver, [$this, $value]); + } + + if (! $this->unique) { + return $value; + } + + $match = $this->model + ->newQuery() + ->when( + in_array(SoftDeletes::class, class_uses_recursive($this->model)), + static function (Builder $query): Builder { + return $query->withTrashed(); + } + ) + ->whereRaw(sprintf( + "`%s` regexp '^%s(%s[\\\\d]+)?$'", + $this->to, + preg_quote($value), + preg_quote($this->separator) + )) + ->orderByDesc($this->to) + ->limit(1) + ->value($this->to); + + $value = is_null($match) ? $value : preg_replace_callback( + sprintf('/%s([\d]+)?$/', preg_quote($this->separator)), + static function (array $match): string { + return str_replace($match[1], (string) (((int) $match[1]) + 1), $match[0]); + }, + $match + ); + + return $value === $match ? sprintf('%s%s1', $value, $this->separator) : $value; + } + + /** + * Get the string representation of the slug. + */ + public function __toString(): string + { + return $this->generate(); + } +} diff --git a/src/Widgets/Widgets.php b/src/Widgets/Widgets.php index bff732c21..6bf2a6f49 100644 --- a/src/Widgets/Widgets.php +++ b/src/Widgets/Widgets.php @@ -6,6 +6,9 @@ use Illuminate\Support\Collection; use Illuminate\Support\Traits\ForwardsCalls; +/** + * @mixin \Illuminate\Support\Collection + */ class Widgets { use ForwardsCalls;