diff --git a/src/Laravel/src/DependencyInjection/MoonShine.php b/src/Laravel/src/DependencyInjection/MoonShine.php index 3cc9ee0f5..d0eb1f7b6 100644 --- a/src/Laravel/src/DependencyInjection/MoonShine.php +++ b/src/Laravel/src/DependencyInjection/MoonShine.php @@ -7,6 +7,7 @@ use MoonShine\Contracts\Core\DependencyInjection\StorageContract; use MoonShine\Core\Core; use MoonShine\Core\Storage\FileStorage; +use MoonShine\Laravel\Fields\Relationships\ModelRelationField; /** * @extends Core @@ -58,4 +59,11 @@ public function getStorage(...$parameters): StorageContract { return app()->make(StorageContract::class, $parameters) ?? new FileStorage(); } + + public function flushState(): void + { + parent::flushState(); + + ModelRelationField::$excludeInstancing = []; + } } diff --git a/src/Laravel/src/Fields/Relationships/ModelRelationField.php b/src/Laravel/src/Fields/Relationships/ModelRelationField.php index 969326f62..25c12ced1 100644 --- a/src/Laravel/src/Fields/Relationships/ModelRelationField.php +++ b/src/Laravel/src/Fields/Relationships/ModelRelationField.php @@ -43,6 +43,8 @@ abstract class ModelRelationField extends Field implements HasResourceContract protected bool $isMorph = false; + public static array $excludeInstancing = []; + /** * @throws Throwable */ @@ -76,7 +78,7 @@ public function __construct( ->singular() ->snake() ->append('_id') - ->value() + ->value(), ); } @@ -89,11 +91,27 @@ public function __construct( } // required to create field entities and load assets - if ($this instanceof HasFieldsContract && ! $this->isMorph()) { + if ($this instanceof HasFieldsContract && ! $this->isExcludeInstancing() && ! $this->isMorph()) { + $this->excludeInstancing(); $this->getResource()?->getFormFields(); } } + public function excludeInstancing(): void + { + self::$excludeInstancing[$this->getExcludeInstanceName()] = true; + } + + private function getExcludeInstanceName(): string + { + return class_basename($this) . $this->getRelationName(); + } + + private function isExcludeInstancing(): bool + { + return isset(self::$excludeInstancing[$this->getExcludeInstanceName()]); + } + /** * @param ?class-string $classString * @throws Throwable @@ -112,13 +130,13 @@ protected function findResource(?string $classString = null): ModelResource ->singular() ->append('Resource') ->kebab() - ->value() + ->value(), ); if (\is_null($resource) && $this->isMorph()) { /** @var ModelResource $resource */ $resource = moonshine()->getResources()->findByUri( - moonshineRequest()->getResourceUri() + moonshineRequest()->getResourceUri(), ); } @@ -127,9 +145,9 @@ protected function findResource(?string $classString = null): ModelResource function (?ModelResource $resource): void { throw_if( \is_null($resource), - FieldException::resourceRequired(static::class, $this->getRelationName()) + FieldException::resourceRequired(static::class, $this->getRelationName()), ); - } + }, ); } @@ -156,15 +174,15 @@ protected function resolveFill(array $raw = [], ?DataWrapperContract $casted = n if ($this->isToOne()) { $this->setColumn( - $this->getRelation()?->getForeignKeyName() ?? '' + $this->getRelation()?->getForeignKeyName() ?? '', ); $this->setRawValue( - $raw[$this->getColumn()] ?? null + $raw[$this->getColumn()] ?? null, ); $this->setFormattedValue( - data_get($data, $this->getResourceColumn()) + data_get($data, $this->getResourceColumn()), ); } @@ -185,8 +203,8 @@ public function toFormattedValue(): mixed $this->getFormattedValueCallback(), $value ?? $this->getRelation()?->getModel(), $this->getRowIndex(), - $this - ) + $this, + ), ); } diff --git a/src/Laravel/src/Traits/Fields/BelongsToOrManyCreatable.php b/src/Laravel/src/Traits/Fields/BelongsToOrManyCreatable.php index 20ee439da..7cd9c48c4 100644 --- a/src/Laravel/src/Traits/Fields/BelongsToOrManyCreatable.php +++ b/src/Laravel/src/Traits/Fields/BelongsToOrManyCreatable.php @@ -7,6 +7,7 @@ use Closure; use MoonShine\Contracts\UI\ActionButtonContract; use MoonShine\Laravel\Buttons\BelongsToOrManyButton; +use MoonShine\Laravel\Fields\Relationships\BelongsToMany; use Throwable; trait BelongsToOrManyCreatable @@ -43,6 +44,10 @@ public function getCreateButton(): ?ActionButtonContract return null; } + if ($this->getParent() instanceof BelongsToMany) { + return null; + } + $button = BelongsToOrManyButton::for($this, button: $this->creatableButton); return $button->isSee() diff --git a/src/UI/src/Fields/FormElement.php b/src/UI/src/Fields/FormElement.php index 71296f84b..223aaed53 100644 --- a/src/UI/src/Fields/FormElement.php +++ b/src/UI/src/Fields/FormElement.php @@ -73,6 +73,8 @@ abstract class FormElement extends MoonShineComponent implements FormElementCont protected static ?Closure $requestValueResolver = null; + protected ?Closure $onRequestValue = null; + protected ?string $requestKeyPrefix = null; protected bool $hasOld = true; @@ -520,18 +522,40 @@ public static function requestValueResolver(Closure $resolver): void static::$requestValueResolver = $resolver; } + /** + * @param Closure(mixed $value, string $name, mixed $default, static $ctx): mixed $callback + */ + public function onRequestValue(Closure $callback): static + { + $this->onRequestValue = $callback; + + return $this; + } + public function getRequestValue(string|int|null $index = null): mixed { if (! \is_null(static::$requestValueResolver)) { return \call_user_func(static::$requestValueResolver, $index, $this->getDefaultIfExists(), $this); } - return $this->prepareRequestValue( + $value = $this->prepareRequestValue( $this->getCore()->getRequest()->get( $this->getRequestNameDot($index), $this->getDefaultIfExists() ) ?? false ); + + if ($this->onRequestValue instanceof Closure) { + return \call_user_func( + $this->onRequestValue, + $value, + $this->getRequestNameDot($index), + $this->getDefaultIfExists(), + $this + ); + } + + return $value; } public function getRequestNameDot(string|int|null $index = null): string