diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php
index 71413623..dfd6f0fa 100644
--- a/resources/views/dashboard.blade.php
+++ b/resources/views/dashboard.blade.php
@@ -7,7 +7,7 @@
@section('content')
@foreach($widgets as $widget)
- {!! $widget !!}
+ @include($widget['template'], $widget)
@endforeach
@endsection
diff --git a/resources/views/resources/index.blade.php b/resources/views/resources/index.blade.php
index 9a42d0d2..001ea0c0 100644
--- a/resources/views/resources/index.blade.php
+++ b/resources/views/resources/index.blade.php
@@ -18,7 +18,7 @@
@if(! empty($widgets))
@foreach($widgets as $widget)
- {!! $widget !!}
+ @include($widget['template'], $widget)
@endforeach
@endif
diff --git a/src/Actions/Action.php b/src/Actions/Action.php
index 55386ee7..fef18a6b 100644
--- a/src/Actions/Action.php
+++ b/src/Actions/Action.php
@@ -91,14 +91,6 @@ public function getModalKey(): string
return sprintf('action-%s', $this->getKey());
}
- /**
- * Get the Blade template.
- */
- public function getTemplate(): string
- {
- return $this->template;
- }
-
/**
* Set the Eloquent query.
*/
@@ -213,7 +205,7 @@ public function toArray(): array
'key' => $this->getKey(),
'modalKey' => $this->getModalKey(),
'name' => $this->getName(),
- 'template' => $this->getTemplate(),
+ 'template' => $this->template,
'url' => $this->getUri(),
];
}
diff --git a/src/Fields/Editor.php b/src/Fields/Editor.php
index 0119d8f1..f6156515 100644
--- a/src/Fields/Editor.php
+++ b/src/Fields/Editor.php
@@ -14,10 +14,10 @@
class Editor extends Field
{
+ use ResolvesFields;
use RegistersRoutes {
RegistersRoutes::registerRoutes as __registerRoutes;
}
- use ResolvesFields;
/**
* The Blade template.
@@ -43,6 +43,7 @@ public function __construct(string $label, string $modelAttribute = null)
$this->config = Config::get('root.editor', []);
$this->height('350px');
+ $this->hiddenOn(['index']);
}
/**
diff --git a/src/Fields/Field.php b/src/Fields/Field.php
index dce6f015..9dbfc7c9 100644
--- a/src/Fields/Field.php
+++ b/src/Fields/Field.php
@@ -128,14 +128,6 @@ public function __construct(string $label, string $modelAttribute = null)
$this->setAttribute('class', 'form-control');
}
- /**
- * Get the template.
- */
- public function getTemplate(): string
- {
- return $this->template;
- }
-
/**
* Get the model attribute.
*/
@@ -538,7 +530,7 @@ public function toArray(): array
'label' => $this->label,
'prefix' => $this->prefix,
'suffix' => $this->suffix,
- 'template' => $this->getTemplate(),
+ 'template' => $this->template,
'searchable' => $this->isSearchable(),
'sortable' => $this->isSortable(),
];
diff --git a/src/Fields/ID.php b/src/Fields/ID.php
index fee71343..4bd1d5e8 100644
--- a/src/Fields/ID.php
+++ b/src/Fields/ID.php
@@ -10,5 +10,7 @@ class ID extends Field
public function __construct(string $label = 'ID', string $modelAttribute = 'id')
{
parent::__construct($label, $modelAttribute);
+
+ $this->hiddenOn(['crate', 'update']);
}
}
diff --git a/src/Fields/Meta.php b/src/Fields/Meta.php
index 2d0e7703..22537794 100644
--- a/src/Fields/Meta.php
+++ b/src/Fields/Meta.php
@@ -55,14 +55,14 @@ public function as(string $field, Closure $callback = null): static
{
$this->field = new $field($this->label, $this->getModelAttribute());
- if (! is_null($callback)) {
- call_user_func_array($callback, [$this->field]);
- }
-
$this->field->value(function (Request $request, Model $model): mixed {
return $this->resolveValue($request, $model);
});
+ if (! is_null($callback)) {
+ call_user_func_array($callback, [$this->field]);
+ }
+
return $this;
}
@@ -249,6 +249,14 @@ public function toArray(): array
return $this->field->toArray();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function toDisplay(Request $request, Model $model): array
+ {
+ return $this->field->toDisplay($request, $model);
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Fields/Relation.php b/src/Fields/Relation.php
index 92749e83..b72e7825 100644
--- a/src/Fields/Relation.php
+++ b/src/Fields/Relation.php
@@ -201,6 +201,30 @@ public function getValue(Model $model): mixed
return $model->getAttribute($name);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function resolveFormat(Request $request, Model $model): mixed
+ {
+ if (is_null($this->formatResolver)) {
+ $this->formatResolver = function (Request $request, Model $model): mixed {
+ $default = $this->getValue($model);
+
+ if ($default instanceof Model) {
+ return $this->resolveDisplay($default);
+ } elseif ($default instanceof Collection) {
+ return $default->map(function (Model $related): mixed {
+ return $this->resolveDisplay($related);
+ })->join(', ');
+ }
+
+ return $default;
+ };
+ }
+
+ return parent::resolveFormat($request, $model);
+ }
+
/**
* Set the query resolver.
*/
diff --git a/src/Http/Controllers/DashboardController.php b/src/Http/Controllers/DashboardController.php
index 57d07e1b..258cf5c8 100644
--- a/src/Http/Controllers/DashboardController.php
+++ b/src/Http/Controllers/DashboardController.php
@@ -15,7 +15,7 @@ class DashboardController extends Controller
public function __invoke(Request $request, Root $root): Response
{
return ResponseFactory::view('root::dashboard', [
- 'widgets' => $root->widgets->all(),
+ 'widgets' => $root->widgets->toArray(),
]);
}
}
diff --git a/src/Resources/Resource.php b/src/Resources/Resource.php
index d6eccbde..1c362c06 100644
--- a/src/Resources/Resource.php
+++ b/src/Resources/Resource.php
@@ -274,7 +274,10 @@ public function paginate(Request $request): LengthAwarePaginator
'id' => $model->getKey(),
'url' => $this->modelUrl($model),
'model' => $model,
- 'fields' => $this->resolveFields($request)->mapToDisplay($request, $model),
+ 'fields' => $this->resolveFields($request)
+ ->authorized($request, $model)
+ ->visible('index')
+ ->mapToDisplay($request, $model),
];
});
}
@@ -315,11 +318,18 @@ public function toIndex(Request $request): array
{
return array_merge($this->toArray(), [
'title' => $this->getName(),
- 'actions' => $this->resolveActions($request)->mapToForms($request),
+ 'actions' => $this->resolveActions($request)
+ ->authorized($request)
+ ->visible('index')
+ ->mapToForms($request),
'data' => $this->paginate($request),
- 'widgets' => $this->resolveWidgets($request)->all(),
+ 'widgets' => $this->resolveWidgets($request)
+ ->authorized($request)
+ ->visible('index')
+ ->toArray(),
'perPageOptions' => $this->getPerPageOptions(),
'filters' => $this->resolveFilters($request)
+ ->authorized($request)
->renderable()
->map(function (RenderableFilter $filter) use ($request): array {
return $filter->toField()->toInput($request, $this->getModelInstance());
@@ -339,7 +349,10 @@ public function toCreate(Request $request): array
'model' => $model = $this->getModelInstance(),
'action' => $this->getUri(),
'method' => 'POST',
- 'fields' => $this->resolveFields($request)->mapToInputs($request, $model),
+ 'fields' => $this->resolveFields($request)
+ ->authorized($request, $model)
+ ->visible('update')
+ ->mapToInputs($request, $model),
]);
}
@@ -349,11 +362,14 @@ public function toCreate(Request $request): array
public function toEdit(Request $request, Model $model): array
{
return array_merge($this->toArray(), [
- 'title' => '',
+ 'title' => __('Edit :model', ['model' => sprintf('%s #%s', $this->getModelName(), $model->getKey())]),
'model' => $model,
'action' => $this->modelUrl($model),
'method' => 'PATCH',
- 'fields' => $this->resolveFields($request)->mapToInputs($request, $model),
+ 'fields' => $this->resolveFields($request)
+ ->authorized($request, $model)
+ ->visible('update')
+ ->mapToInputs($request, $model),
]);
}
}
diff --git a/src/Widgets/Widget.php b/src/Widgets/Widget.php
index 4aedd984..6f14bfe6 100644
--- a/src/Widgets/Widget.php
+++ b/src/Widgets/Widget.php
@@ -2,15 +2,20 @@
namespace Cone\Root\Widgets;
-use Cone\Root\Support\Element;
use Cone\Root\Traits\Authorizable;
+use Cone\Root\Traits\HasAttributes;
use Cone\Root\Traits\Makeable;
+use Cone\Root\Traits\ResolvesVisibility;
+use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Str;
+use JsonSerializable;
-abstract class Widget extends Element
+abstract class Widget implements Arrayable, JsonSerializable
{
use Authorizable;
+ use HasAttributes;
use Makeable;
+ use ResolvesVisibility;
/**
* The Blade template.
@@ -33,14 +38,24 @@ public function getName(): string
return __(Str::of(static::class)->classBasename()->headline()->value());
}
+ /**
+ * Convert the element to a JSON serializable format.
+ */
+ public function jsonSerialize(): mixed
+ {
+ return $this->toArray();
+ }
+
/**
* Convert the widget to an array.
*/
public function toArray(): array
{
return [
+ 'attrs' => $this->newAttributeBag(),
'key' => $this->getKey(),
'name' => $this->getName(),
+ 'template' => $this->template,
];
}
}
diff --git a/src/Widgets/Widgets.php b/src/Widgets/Widgets.php
index bff732c2..e559d0b9 100644
--- a/src/Widgets/Widgets.php
+++ b/src/Widgets/Widgets.php
@@ -2,56 +2,38 @@
namespace Cone\Root\Widgets;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
-use Illuminate\Support\Traits\ForwardsCalls;
-class Widgets
+class Widgets extends Collection
{
- use ForwardsCalls;
-
- /**
- * The widgets collection.
- */
- protected Collection $widgets;
-
- /**
- * Create a new widgets instance.
- */
- public function __construct(array $widgets = [])
- {
- $this->widgets = new Collection($widgets);
- }
-
/**
* Register the given widgets.
*/
public function register(array|Widget $widgets): static
{
foreach (Arr::wrap($widgets) as $widget) {
- $this->widgets->push($widget);
+ $this->push($widget);
}
return $this;
}
/**
- * Make a new widget instance.
+ * Filter the fields that are available for the current request and model.
*/
- public function widget(string $widget, ...$params): Widget
+ public function authorized(Request $request, Model $model = null): static
{
- $instance = new $widget(...$params);
-
- $this->register($instance);
-
- return $instance;
+ return $this->filter->authorized($request, $model)->values();
}
/**
- * Handle the dynamic method call.
+ * Filter the fields that are visible in the given context.
*/
- public function __call($method, $parameters): mixed
+ public function visible(string|array $context): static
{
- return $this->forwardCallTo($this->widgets, $method, $parameters);
+ return $this->filter->visible($context)->values();
}
}