diff --git a/demo/app/Sharp/Entities/PostBlockEntity.php b/demo/app/Sharp/Entities/PostBlockEntity.php
index 2d5c3891a..668c1bdfc 100644
--- a/demo/app/Sharp/Entities/PostBlockEntity.php
+++ b/demo/app/Sharp/Entities/PostBlockEntity.php
@@ -15,12 +15,12 @@ class PostBlockEntity extends SharpEntity
protected ?string $policy = PostBlockPolicy::class;
protected string $label = 'Block';
- public function getMultiforms(): array
- {
- return [
- 'text' => [PostBlockTextForm::class, 'Text block'],
- 'visuals' => [PostBlockVisualsForm::class, 'Visuals block'],
- 'video' => [PostBlockVideoForm::class, 'Video block'],
- ];
- }
+ // public function getMultiforms(): array
+ // {
+ // return [
+ // 'text' => [PostBlockTextForm::class, 'Text block'],
+ // 'visuals' => [PostBlockVisualsForm::class, 'Visuals block'],
+ // 'video' => [PostBlockVideoForm::class, 'Video block'],
+ // ];
+ // }
}
diff --git a/demo/app/Sharp/Entities/PostBlockTextEntity.php b/demo/app/Sharp/Entities/PostBlockTextEntity.php
new file mode 100644
index 000000000..0eecbf49f
--- /dev/null
+++ b/demo/app/Sharp/Entities/PostBlockTextEntity.php
@@ -0,0 +1,12 @@
+configureMultiformAttribute('type')
+ $this->configureEntityMap(
+ attribute: 'type',
+ entities: EntityListEntities::make()
+ ->addEntity('text', PostBlockTextEntity::class)
+ ->addEntity('video', PostBlockVideoEntity::class)
+ ->addEntity('visuals', PostBlockVisualsEntity::class),
+ )
->configureReorderable(new SimpleEloquentReorderHandler(PostBlock::class))
->configureQuickCreationForm();
}
diff --git a/demo/app/Sharp/Posts/Blocks/PostBlockVisualsShow.php b/demo/app/Sharp/Posts/Blocks/PostBlockVisualsShow.php
new file mode 100644
index 000000000..18e06f07c
--- /dev/null
+++ b/demo/app/Sharp/Posts/Blocks/PostBlockVisualsShow.php
@@ -0,0 +1,62 @@
+addField(
+ SharpShowListField::make('files')
+ ->setLabel('Visuals')
+ ->addItemField(
+ SharpShowFileField::make('file')
+ )
+ ->addItemField(
+ SharpShowTextField::make('legend')
+ ->setLabel('Legend')
+ )
+ );
+ }
+
+ protected function buildShowLayout(ShowLayout $showLayout): void
+ {
+ $showLayout
+ ->addSection(function (ShowLayoutSection $section) {
+ $section
+ ->addColumn(12, function (ShowLayoutColumn $column) {
+ $column
+ ->withListField('files', function (ShowLayoutColumn $item) {
+ $item->withFields('file')
+ ->withField('legend');
+ });
+ });
+ });
+ }
+
+ public function find(mixed $id): array
+ {
+ $postBlock = PostBlock::findOrFail($id);
+
+ return $this
+ ->setCustomTransformer('files', new SharpUploadModelFormAttributeTransformer())
+ ->transform($postBlock);
+ }
+
+ public function delete(mixed $id): void
+ {
+ PostBlock::findOrFail($id)->delete();
+ }
+}
diff --git a/resources/js/Layouts/Layout.vue b/resources/js/Layouts/Layout.vue
index 8b48edf9c..abb9c0dd4 100644
--- a/resources/js/Layouts/Layout.vue
+++ b/resources/js/Layouts/Layout.vue
@@ -219,8 +219,8 @@
-
-
+
+
{{ __('sharp::action_bar.color-mode-dropdown.label') }}
@@ -233,7 +233,7 @@
diff --git a/resources/js/Pages/Show/Show.vue b/resources/js/Pages/Show/Show.vue
index 88a06ee60..703498790 100644
--- a/resources/js/Pages/Show/Show.vue
+++ b/resources/js/Pages/Show/Show.vue
@@ -335,7 +335,7 @@
:collapsable="section.collapsable"
:entity-key="entityKey"
:instance-id="instanceId"
- :is-right-col="columnIndex === section.columns.length - 1"
+ :is-right-col="columnIndex > 0 && columnIndex === section.columns.length - 1"
:row="row"
@reordering="onEntityListReordering(fieldLayout.key, $event)"
/>
diff --git a/resources/js/entity-list/EntityList.ts b/resources/js/entity-list/EntityList.ts
index 14d9ecfbb..25f55f146 100644
--- a/resources/js/entity-list/EntityList.ts
+++ b/resources/js/entity-list/EntityList.ts
@@ -6,7 +6,6 @@ import {
EntityStateValueData,
FilterData,
} from "@/types";
-import { getAppendableParentUri, route } from "@/utils/url";
import { EntityListInstance, InstanceId } from "./types";
export class EntityList implements EntityListData {
@@ -14,7 +13,7 @@ export class EntityList implements EntityListData {
config: EntityListData['config'];
data: EntityListData['data'];
fields: EntityListData['fields'];
- forms: EntityListData['forms'];
+ entities: EntityListData['entities'];
meta: EntityListData['meta'];
pageAlert: EntityListData['pageAlert'];
query: EntityListData['query'];
@@ -110,31 +109,6 @@ export class EntityList implements EntityListData {
return instance[this.config.instanceIdAttribute];
}
- instanceUrl(instance: EntityListInstance): string | null {
- const entityKey = this.entityKey;
- const instanceId = this.instanceId(instance);
-
- if(!this.authorizations.view.includes(instanceId)) {
- return null;
- }
-
- const multiform = this.forms && Object.values(this.forms).find(form => form.instances.includes(instanceId));
-
- if(this.config.hasShowPage) {
- return route('code16.sharp.show.show', {
- parentUri: getAppendableParentUri(),
- entityKey: multiform ? `${entityKey}:${multiform.key}` : entityKey,
- instanceId,
- });
- }
-
- return route('code16.sharp.form.edit', {
- parentUri: getAppendableParentUri(),
- entityKey: multiform ? `${entityKey}:${multiform.key}` : entityKey,
- instanceId,
- });
- }
-
instanceState(instance: EntityListInstance): string | number | null {
return this.config.state
? instance[this.config.state.attribute]
@@ -153,10 +127,7 @@ export class EntityList implements EntityListData {
}
instanceCanDelete(instance: EntityListInstance): boolean {
- if(Array.isArray(this.authorizations.delete)) {
- return this.authorizations.delete?.includes(this.instanceId(instance));
- }
- return !!this.authorizations.delete;
+ return instance._meta.authorizations.delete;
}
instanceCommands(instance: EntityListInstance): Array> | undefined {
diff --git a/resources/js/entity-list/components/EntityList.vue b/resources/js/entity-list/components/EntityList.vue
index 2c858ff8c..805f85d31 100644
--- a/resources/js/entity-list/components/EntityList.vue
+++ b/resources/js/entity-list/components/EntityList.vue
@@ -3,9 +3,9 @@
import { FilterManager } from "@/filters/FilterManager";
import { EntityList } from "../EntityList";
import {
- CommandData, EntityListFieldData, EntityListMultiformData, EntityListQueryParamsData,
+ CommandData, EntityListFieldData, EntityListQueryParamsData,
EntityStateValueData,
- FilterData
+ FilterData, EntityListEntityData
} from "@/types";
import WithCommands from "@/commands/components/WithCommands.vue";
import { CommandManager } from "@/commands/CommandManager";
@@ -68,6 +68,7 @@
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useElementVisibility } from "@vueuse/core";
import StateBadge from "@/components/ui/StateBadge.vue";
+ import Icon from "@/components/ui/Icon.vue";
// import StateBadgeTest from "@/components/dev/StateBadgeTest.vue";
const props = withDefaults(defineProps<{
@@ -175,7 +176,7 @@
});
}
- async function onCreate(event: MouseEvent, form?: EntityListMultiformData) {
+ async function onCreate(event: MouseEvent, listEntity?: EntityListEntityData) {
if(event.metaKey || event.ctrlKey || event.shiftKey) {
return;
}
@@ -186,19 +187,18 @@
if(props.entityList.config.quickCreationForm) {
await props.commands.send({ hasForm: true } as CommandData, {
postCommand: route('code16.sharp.api.list.command.quick-creation-form.store', {
- entityKey: form ? `${entityKey}:${form.key}` : entityKey,
+ entityKey,
+ formEntityKey: listEntity ? listEntity.entityKey : entityKey,
}),
getForm: route('code16.sharp.api.list.command.quick-creation-form.create', {
- entityKey: form ? `${entityKey}:${form.key}` : entityKey,
+ entityKey,
+ formEntityKey: listEntity ? listEntity.entityKey : entityKey,
}),
query: props.entityList.query,
- entityKey: form ? `${entityKey}:${form.key}` : entityKey,
+ entityKey: listEntity ? listEntity.entityKey : entityKey,
});
} else {
- router.visit(route('code16.sharp.form.create', {
- parentUri: getAppendableParentUri(),
- entityKey: form ? `${entityKey}:${form.key}` : entityKey,
- }));
+ router.visit(listEntity ? listEntity.formCreateUrl : props.entityList.config.formCreateUrl);
}
}
@@ -469,7 +469,7 @@
-
+
-
+
- {{ form.label }}
+
+
+
+ {{ entity.label }}
@@ -786,10 +789,10 @@
{{ item[field.key] }}
-
+
diff --git a/resources/js/types/generated.d.ts b/resources/js/types/generated.d.ts
index e33b0dd8c..2f69f578c 100644
--- a/resources/js/types/generated.d.ts
+++ b/resources/js/types/generated.d.ts
@@ -133,8 +133,6 @@ export type EmbedFormData = {
layout: FormLayoutData | null;
};
export type EntityListAuthorizationsData = {
- view: Array;
- delete: Array;
create: boolean;
reorder: boolean;
};
@@ -147,7 +145,8 @@ export type EntityListConfigData = {
hasShowPage: boolean;
deleteConfirmationText: string;
deleteHidden: boolean;
- multiformAttribute: string | null;
+ formCreateUrl: string;
+ entityAttribute: string | null;
createButtonLabel: string | null;
quickCreationForm: boolean;
filters: ConfigFiltersData | null;
@@ -159,13 +158,20 @@ export type EntityListData = {
authorizations: EntityListAuthorizationsData;
config: EntityListConfigData;
fields: Array;
- data: Array<{ [key: string]: any }>;
+ data: Array<{ [key: string]: any; _meta: EntityListItemMeta }>;
filterValues: FilterValuesData;
query: EntityListQueryParamsData | null;
- forms: { [key: string]: EntityListMultiformData } | null;
+ entities: Array | null;
meta: PaginatorMetaData | null;
pageAlert: PageAlertData | null;
};
+export type EntityListEntityData = {
+ key: string;
+ entityKey: string;
+ label: string;
+ icon: IconData | null;
+ formCreateUrl: string | null;
+};
export type EntityListFieldData = {
type: "text" | "state" | "badge";
key: string;
@@ -176,10 +182,9 @@ export type EntityListFieldData = {
html: boolean | null;
tooltip: string | null;
};
-export type EntityListMultiformData = {
- key: string;
- label: string;
- instances: Array;
+export type EntityListItemMeta = {
+ url: string | null;
+ authorizations: InstanceAuthorizationsData;
};
export type EntityListQueryParamsData = {
search?: string;
diff --git a/resources/js/types/routes.d.ts b/resources/js/types/routes.d.ts
index 31664ff36..ed41fe615 100644
--- a/resources/js/types/routes.d.ts
+++ b/resources/js/types/routes.d.ts
@@ -127,12 +127,20 @@ declare module 'ziggy-js' {
{
"name": "entityKey",
"binding": "key"
+ },
+ {
+ "name": "formEntityKey",
+ "binding": "key"
}
],
"code16.sharp.api.list.command.quick-creation-form.store": [
{
"name": "entityKey",
"binding": "key"
+ },
+ {
+ "name": "formEntityKey",
+ "binding": "key"
}
],
"code16.sharp.api.list": [
diff --git a/src/Config/SharpConfigBuilder.php b/src/Config/SharpConfigBuilder.php
index 6ec709234..b2a34d751 100644
--- a/src/Config/SharpConfigBuilder.php
+++ b/src/Config/SharpConfigBuilder.php
@@ -153,7 +153,18 @@ public function declareEntity(string $entityClass): self
);
}
- $entityKey = $entityClass::$entityKey ?? null;
+ $entityKey = $entityClass::$entityKey;
+
+ if (isset($entityClass::$entityKey)
+ && (get_parent_class($entityClass)::$entityKey ?? null) === $entityClass::$entityKey) {
+ throw new SharpInvalidEntityKeyException(
+ sprintf(
+ '%s has same entity key than its parent class: %s. Specify a new one or remove entity keys completely.',
+ $entityClass, get_parent_class($entityClass)
+ )
+ );
+ }
+
if (! $entityKey) {
$entityKey = str(class_basename($entityClass))
->beforeLast('Entity')
diff --git a/src/Data/EntityList/EntityListConfigData.php b/src/Data/EntityList/EntityListConfigData.php
index 361dbb579..d6f91e051 100644
--- a/src/Data/EntityList/EntityListConfigData.php
+++ b/src/Data/EntityList/EntityListConfigData.php
@@ -21,7 +21,8 @@ public function __construct(
public bool $hasShowPage,
public string $deleteConfirmationText,
public bool $deleteHidden,
- public ?string $multiformAttribute,
+ public string $formCreateUrl,
+ public ?string $entityAttribute,
public ?string $createButtonLabel = null,
public bool $quickCreationForm = false,
public ?ConfigFiltersData $filters = null,
diff --git a/src/Data/EntityList/EntityListData.php b/src/Data/EntityList/EntityListData.php
index cccd40c27..eecbe40d6 100644
--- a/src/Data/EntityList/EntityListData.php
+++ b/src/Data/EntityList/EntityListData.php
@@ -20,12 +20,12 @@ public function __construct(
public EntityListConfigData $config,
/** @var EntityListFieldData[] */
public array $fields,
- #[LiteralTypeScriptType('Array<{ [key: string]: any }>')]
+ #[LiteralTypeScriptType('Array<{ [key: string]: any, _meta: EntityListItemMeta }>')]
public array $data,
public FilterValuesData $filterValues,
public ?EntityListQueryParamsData $query,
- /** @var array|null */
- public ?array $forms = null,
+ /** @var EntityListEntityData[]|null */
+ public ?array $entities = null,
public ?PaginatorMetaData $meta = null,
public ?PageAlertData $pageAlert = null,
) {}
@@ -40,7 +40,9 @@ public static function from(array $entityList): self
data: $entityList['data'],
filterValues: FilterValuesData::from($entityList['filterValues']),
query: EntityListQueryParamsData::optional($entityList['query']),
- forms: $entityList['forms'] ? EntityListMultiformData::collection($entityList['forms']) : null,
+ entities: $entityList['entities']
+ ? EntityListEntityData::collection($entityList['entities'])
+ : null,
meta: PaginatorMetaData::optional($entityList['meta'] ?? null),
pageAlert: PageAlertData::optional($entityList['pageAlert'] ?? null),
);
diff --git a/src/Data/EntityList/EntityListEntityData.php b/src/Data/EntityList/EntityListEntityData.php
new file mode 100644
index 000000000..dcb678702
--- /dev/null
+++ b/src/Data/EntityList/EntityListEntityData.php
@@ -0,0 +1,27 @@
+ */
- public array $instances,
- ) {}
-}
diff --git a/src/Data/EntityListAuthorizationsData.php b/src/Data/EntityListAuthorizationsData.php
index 856a2f5f3..0f59ed69b 100644
--- a/src/Data/EntityListAuthorizationsData.php
+++ b/src/Data/EntityListAuthorizationsData.php
@@ -8,11 +8,12 @@
final class EntityListAuthorizationsData extends Data
{
public function __construct(
- /** @var array */
- public array $view,
- /** @var array */
- public array $delete,
public bool $create,
- public bool $reorder,
+ public bool $reorder = false,
) {}
+
+ public static function from(array $authorizations): self
+ {
+ return new self(...$authorizations);
+ }
}
diff --git a/src/EntityList/Commands/QuickCreate/QuickCreationCommand.php b/src/EntityList/Commands/QuickCreate/QuickCreationCommand.php
index f9e1e356b..0de23d327 100644
--- a/src/EntityList/Commands/QuickCreate/QuickCreationCommand.php
+++ b/src/EntityList/Commands/QuickCreate/QuickCreationCommand.php
@@ -7,7 +7,6 @@
use Code16\Sharp\Form\Fields\SharpFormField;
use Code16\Sharp\Form\SharpForm;
use Code16\Sharp\Utils\Fields\FieldsContainer;
-use Illuminate\Support\Uri;
class QuickCreationCommand extends EntityCommand
{
@@ -74,13 +73,12 @@ public function getInstanceId(): mixed
public function execute(array $data = []): array
{
- $currentUrl = sharp()->context()->breadcrumb()->getCurrentSegmentUrl();
-
- sharp()->context()->breadcrumb()->forceRequestSegments(
- str(Uri::of($currentUrl)->path())
+ $breadcrumb = sharp()->context()->breadcrumb();
+ $currentPageUrl = $breadcrumb->getCurrentSegmentUrl();
+ $breadcrumb->forceRequestSegments(
+ str($breadcrumb->getCurrentPath())
->explode('/')
->filter()
- ->skip(1)
->concat(['s-form', $this->entityKey])
);
$this->instanceId = $this->sharpForm->update(null, $data);
@@ -90,7 +88,7 @@ public function execute(array $data = []): array
}
return $this->sharpForm->isDisplayShowPageAfterCreation()
- ? $this->link(sprintf('%s/s-show/%s/%s', $currentUrl, $this->entityKey, $this->instanceId))
+ ? $this->link(sprintf('%s/s-show/%s/%s', $currentPageUrl, $this->entityKey, $this->instanceId))
: $this->reload();
}
diff --git a/src/EntityList/EntityListEntities.php b/src/EntityList/EntityListEntities.php
new file mode 100644
index 000000000..4564c0dbb
--- /dev/null
+++ b/src/EntityList/EntityListEntities.php
@@ -0,0 +1,39 @@
+entities[$key] = new EntityListEntity($entityKeyOrClassName, $icon, $label);
+
+ return $this;
+ }
+
+ public function find(string $key): ?EntityListEntity
+ {
+ return $this->entities[$key] ?? null;
+ }
+
+ public function all(): array
+ {
+ return $this->entities;
+ }
+}
diff --git a/src/EntityList/EntityListEntity.php b/src/EntityList/EntityListEntity.php
new file mode 100644
index 000000000..e24919da4
--- /dev/null
+++ b/src/EntityList/EntityListEntity.php
@@ -0,0 +1,38 @@
+entityFor($this->getEntityKey());
+ }
+
+ public function getEntityKey(): string
+ {
+ return app(SharpEntityManager::class)->entityKeyFor($this->entityKeyOrClassName);
+ }
+
+ public function getIcon(): ?string
+ {
+ return $this->icon;
+ }
+
+ public function getLabel(): ?string
+ {
+ return $this->label;
+ }
+}
diff --git a/src/EntityList/SharpEntityList.php b/src/EntityList/SharpEntityList.php
index 74f4f2f0d..bac259936 100644
--- a/src/EntityList/SharpEntityList.php
+++ b/src/EntityList/SharpEntityList.php
@@ -27,7 +27,8 @@ abstract class SharpEntityList
private ?EntityListFieldsContainer $fieldsContainer = null;
protected ?EntityListQueryParams $queryParams;
protected string $instanceIdAttribute = 'id';
- protected ?string $multiformAttribute = null;
+ protected ?string $entityAttribute = null;
+ protected ?EntityListEntities $entities = null;
protected bool $searchable = false;
protected ?ReorderHandler $reorderHandler = null;
private bool $disabledReorder = false;
@@ -84,13 +85,13 @@ final public function data(?array $query = null): array
// Filter model attributes on actual form fields
->map(fn ($row) => collect($row)
->only(
- array_merge(
- array_keys($this->transformers),
- $this->entityStateAttribute ? [$this->entityStateAttribute] : [],
- $this->multiformAttribute ? [$this->multiformAttribute] : [],
- [$this->instanceIdAttribute],
- $this->getDataKeys(),
- ),
+ [
+ ...array_keys($this->transformers),
+ ...$this->entityStateAttribute ? [$this->entityStateAttribute] : [],
+ ...$this->getEntityAttribute() ? [$this->getEntityAttribute()] : [],
+ $this->instanceIdAttribute,
+ ...$this->getDataKeys(),
+ ]
)
->toArray()
)
@@ -108,7 +109,7 @@ final public function listConfig(bool $hasShowPage = false): array
{
$config = [
'instanceIdAttribute' => $this->instanceIdAttribute,
- 'multiformAttribute' => $this->multiformAttribute,
+ 'entityAttribute' => $this->getEntityAttribute(),
'searchable' => $this->searchable,
'reorderable' => ! is_null($this->reorderHandler) && ! $this->disabledReorder,
'defaultSort' => $this->defaultSort,
@@ -135,6 +136,14 @@ final public function configureInstanceIdAttribute(string $instanceIdAttribute):
return $this;
}
+ /**
+ * @internal
+ */
+ final public function getInstanceIdAttribute(): string
+ {
+ return $this->instanceIdAttribute;
+ }
+
final public function configureReorderable(ReorderHandler|string $reorderHandler): self
{
$this->reorderHandler = instanciate($reorderHandler);
@@ -180,13 +189,41 @@ final public function configureDefaultSort(string $sortBy, string $sortDir = 'as
return $this;
}
+ /**
+ * @deprecated
+ * @see self::configureEntityMap()
+ */
final protected function configureMultiformAttribute(?string $attribute): self
{
- $this->multiformAttribute = $attribute;
+ $this->entityAttribute = $attribute;
+
+ return $this;
+ }
+
+ final protected function configureEntityMap(string $attribute, EntityListEntities $entities): self
+ {
+ $this->entityAttribute = $attribute;
+ $this->entities = $entities;
return $this;
}
+ /**
+ * @internal
+ */
+ final public function getEntityAttribute(): ?string
+ {
+ return $this->entityAttribute;
+ }
+
+ /**
+ * @internal
+ */
+ final public function getEntities(): ?EntityListEntities
+ {
+ return $this->entities;
+ }
+
final public function reorderHandler(): ?ReorderHandler
{
return $this->reorderHandler;
diff --git a/src/Http/Context/SharpBreadcrumb.php b/src/Http/Context/SharpBreadcrumb.php
index 80c674fb7..a3f88c956 100644
--- a/src/Http/Context/SharpBreadcrumb.php
+++ b/src/Http/Context/SharpBreadcrumb.php
@@ -62,9 +62,9 @@ public function previousSegment(): ?BreadcrumbItem
return $this->breadcrumbItems()->reverse()->skip(1)->first();
}
- public function previousShowSegment(?string $entityKeyOrClassName = null, ?string $subEntity = null): ?BreadcrumbItem
+ public function previousShowSegment(?string $entityKeyOrClassName = null, ?string $multiformKey = null): ?BreadcrumbItem
{
- return $this->findPreviousSegment('s-show', $entityKeyOrClassName, $subEntity);
+ return $this->findPreviousSegment('s-show', $entityKeyOrClassName, $multiformKey);
}
public function previousListSegment(?string $entityKeyOrClassName = null): ?BreadcrumbItem
@@ -78,13 +78,18 @@ public function getCurrentSegmentUrl(): string
sprintf(
'%s/%s',
sharp()->config()->get('custom_url_segment'),
- $this->breadcrumbItems()
- ->map(fn (BreadcrumbItem $item) => $item->toUri())
- ->implode('/')
+ $this->getCurrentPath()
)
);
}
+ public function getCurrentPath(): ?string
+ {
+ return $this->breadcrumbItems()
+ ->map(fn (BreadcrumbItem $item) => $item->toUri())
+ ->implode('/');
+ }
+
public function getPreviousSegmentUrl(): string
{
return url(
@@ -111,7 +116,7 @@ public function breadcrumbItems(): Collection
return $this->breadcrumbItems;
}
- private function findPreviousSegment(string $type, ?string $entityKeyOrClassName = null, ?string $subEntity = null): ?BreadcrumbItem
+ private function findPreviousSegment(string $type, ?string $entityKeyOrClassName = null, ?string $multiformKey = null): ?BreadcrumbItem
{
$modeNotEquals = false;
if ($entityKeyOrClassName && Str::startsWith($entityKeyOrClassName, '!')) {
@@ -124,8 +129,8 @@ private function findPreviousSegment(string $type, ?string $entityKeyOrClassName
->filter(fn (BreadcrumbItem $item) => $item->type === $type)
->when($entityKeyOrClassName !== null, fn ($items) => $items
->filter(fn (BreadcrumbItem $breadcrumbItem) => $modeNotEquals
- ? ! $breadcrumbItem->entityIs($entityKeyOrClassName, $subEntity)
- : $breadcrumbItem->entityIs($entityKeyOrClassName, $subEntity)
+ ? ! $breadcrumbItem->entityIs($entityKeyOrClassName, $multiformKey)
+ : $breadcrumbItem->entityIs($entityKeyOrClassName, $multiformKey)
)
)
->first();
@@ -243,14 +248,14 @@ private function getEntityLabelForInstance(BreadcrumbItem $item, bool $isLeaf):
return app(SharpEntityManager::class)
->entityFor($item->key)
- ->getLabelOrFail((new EntityKey($item->key))->subEntity());
+ ->getLabelOrFail((new EntityKey($item->key))->multiformKey());
}
private function isSameEntityKeys(string $key1, string $key2, bool $compareBaseEntities): bool
{
if ($compareBaseEntities) {
- $key1 = explode(':', $key1)[0];
- $key2 = explode(':', $key2)[0];
+ $key1 = (new EntityKey($key1))->baseKey();
+ $key2 = (new EntityKey($key2))->baseKey();
}
return $key1 === $key2;
diff --git a/src/Http/Context/Util/BreadcrumbItem.php b/src/Http/Context/Util/BreadcrumbItem.php
index 7d6163085..d3bc74c73 100644
--- a/src/Http/Context/Util/BreadcrumbItem.php
+++ b/src/Http/Context/Util/BreadcrumbItem.php
@@ -68,13 +68,13 @@ public function is(BreadcrumbItem $item): bool
&& $this->instance === $item->instance;
}
- public function entityIs(string $entityKeyOrClassName, ?string $subEntity = null): bool
+ public function entityIs(string $entityKeyOrClassName, ?string $multiformKey = null): bool
{
$selfKey = new EntityKey($this->key);
$resolvedEntityKey = app(SharpEntityManager::class)->entityKeyFor($entityKeyOrClassName);
return $selfKey->baseKey() === $resolvedEntityKey
- && (! $subEntity || $selfKey->subEntity() === $subEntity);
+ && (! $multiformKey || $selfKey->multiformKey() === $multiformKey);
}
public function entityKey(): string
diff --git a/src/Http/Controllers/Api/ApiController.php b/src/Http/Controllers/Api/ApiController.php
index bbca6a693..f605b8b4b 100644
--- a/src/Http/Controllers/Api/ApiController.php
+++ b/src/Http/Controllers/Api/ApiController.php
@@ -32,9 +32,4 @@ protected function getDashboardInstance(string $dashboardKey): ?SharpDashboard
{
return $this->entityManager->entityFor($dashboardKey)->getViewOrFail();
}
-
- protected function isSubEntity(string $entityKey): bool
- {
- return str_contains($entityKey, ':');
- }
}
diff --git a/src/Http/Controllers/Api/ApiFormAutocompleteController.php b/src/Http/Controllers/Api/ApiFormAutocompleteController.php
index 8ea8ee7dc..125a27f02 100644
--- a/src/Http/Controllers/Api/ApiFormAutocompleteController.php
+++ b/src/Http/Controllers/Api/ApiFormAutocompleteController.php
@@ -120,7 +120,7 @@ private function getFieldContainer(EntityKey $entityKey): SharpFormEditorEmbed|C
);
}
- return $entity->getFormOrFail($entityKey->subEntity());
+ return $entity->getFormOrFail($entityKey->multiformKey());
}
private function getFormattedData() {}
diff --git a/src/Http/Controllers/Api/Commands/ApiDashboardCommandController.php b/src/Http/Controllers/Api/Commands/ApiDashboardCommandController.php
index 88ae383e9..360cf5d38 100644
--- a/src/Http/Controllers/Api/Commands/ApiDashboardCommandController.php
+++ b/src/Http/Controllers/Api/Commands/ApiDashboardCommandController.php
@@ -44,7 +44,7 @@ public function update(string $entityKey, string $commandKey)
$commandHandler = $this->getDashboardCommandHandler($dashboard, $commandKey);
$formattedData = $commandHandler->formatAndValidateRequestData((array) request('data'));
- $result = $this->returnCommandResult($dashboard, $commandHandler->execute($formattedData));
+ $result = $this->returnCommandResult($dashboard, $entityKey, $commandHandler->execute($formattedData));
$this->uploadManager->dispatchJobs();
return $result;
diff --git a/src/Http/Controllers/Api/Commands/ApiEntityListEntityCommandController.php b/src/Http/Controllers/Api/Commands/ApiEntityListEntityCommandController.php
index ce91e2cc9..c1e416bfb 100644
--- a/src/Http/Controllers/Api/Commands/ApiEntityListEntityCommandController.php
+++ b/src/Http/Controllers/Api/Commands/ApiEntityListEntityCommandController.php
@@ -42,7 +42,7 @@ public function update(string $entityKey, string $commandKey)
$commandHandler = $this->getEntityCommandHandler($list, $commandKey);
$formattedData = $commandHandler->formatAndValidateRequestData((array) request('data'));
- $result = $this->returnCommandResult($list, $commandHandler->execute($formattedData));
+ $result = $this->returnCommandResult($list, $entityKey, $commandHandler->execute($formattedData));
$this->uploadManager->dispatchJobs();
return $result;
diff --git a/src/Http/Controllers/Api/Commands/ApiEntityListEntityStateController.php b/src/Http/Controllers/Api/Commands/ApiEntityListEntityStateController.php
index 9c05664f4..e15728a00 100644
--- a/src/Http/Controllers/Api/Commands/ApiEntityListEntityStateController.php
+++ b/src/Http/Controllers/Api/Commands/ApiEntityListEntityStateController.php
@@ -23,6 +23,7 @@ public function update(string $entityKey, mixed $instanceId)
return $this->returnCommandResult(
$list,
+ $entityKey,
array_merge(
$list->entityStateHandler()->execute($instanceId, request()->only('value')),
['value' => request('value')],
diff --git a/src/Http/Controllers/Api/Commands/ApiEntityListInstanceCommandController.php b/src/Http/Controllers/Api/Commands/ApiEntityListInstanceCommandController.php
index cbbb8a241..15bd9b603 100644
--- a/src/Http/Controllers/Api/Commands/ApiEntityListInstanceCommandController.php
+++ b/src/Http/Controllers/Api/Commands/ApiEntityListInstanceCommandController.php
@@ -45,7 +45,7 @@ public function update(string $entityKey, string $commandKey, mixed $instanceId)
$commandHandler = $this->getInstanceCommandHandler($list, $commandKey, $instanceId);
$formattedData = $commandHandler->formatAndValidateRequestData((array) request('data'), $instanceId);
- $result = $this->returnCommandResult($list, $commandHandler->execute($instanceId, $formattedData));
+ $result = $this->returnCommandResult($list, $entityKey, $commandHandler->execute($instanceId, $formattedData));
$this->uploadManager->dispatchJobs($instanceId);
return $result;
diff --git a/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php b/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php
index ed31d995b..76150765e 100644
--- a/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php
+++ b/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php
@@ -17,7 +17,7 @@ public function __construct(private readonly SharpUploadManager $uploadManager)
parent::__construct();
}
- public function create(EntityKey $entityKey)
+ public function create(EntityKey $entityKey, EntityKey $formEntityKey)
{
$entity = $this->entityManager->entityFor($entityKey);
@@ -29,14 +29,14 @@ public function create(EntityKey $entityKey)
403
);
- $form = $entity->getFormOrFail($entityKey->subEntity());
+ $form = $this->entityManager->entityFor($formEntityKey)->getFormOrFail($formEntityKey->multiformKey());
$form->buildFormConfig();
$quickCreationHandler
->setEntityKey($entityKey)
->setFormInstance($form)
->setTitle(__('sharp::breadcrumb.form.create_entity', [
- 'entity' => $entity->getLabelOrFail($entityKey->subEntity()),
+ 'entity' => $entity->getLabelOrFail($entityKey->multiformKey()),
]));
$quickCreationHandler->buildCommandConfig();
@@ -48,9 +48,9 @@ public function create(EntityKey $entityKey)
);
}
- public function store(EntityKey $entityKey)
+ public function store(EntityKey $entityKey, EntityKey $formEntityKey)
{
- $list = $this->getListInstance($entityKey);
+ $list = $this->entityManager->entityFor($entityKey)->getListOrFail();
$list->buildListConfig();
abort_if(
@@ -58,7 +58,7 @@ public function store(EntityKey $entityKey)
403
);
- $form = $this->entityManager->entityFor($entityKey)->getFormOrFail($entityKey->subEntity());
+ $form = $this->entityManager->entityFor($formEntityKey)->getFormOrFail($formEntityKey->multiformKey());
$form->buildFormConfig();
$quickCreationHandler
@@ -68,6 +68,7 @@ public function store(EntityKey $entityKey)
$formattedData = $quickCreationHandler->formatAndValidateRequestData((array) request('data'));
$result = $this->returnCommandResult(
$list,
+ $entityKey,
$quickCreationHandler->execute($formattedData)
);
$this->uploadManager->dispatchJobs($quickCreationHandler->getInstanceId());
diff --git a/src/Http/Controllers/Api/Commands/ApiShowEntityStateController.php b/src/Http/Controllers/Api/Commands/ApiShowEntityStateController.php
index 5d13e94d8..343816331 100644
--- a/src/Http/Controllers/Api/Commands/ApiShowEntityStateController.php
+++ b/src/Http/Controllers/Api/Commands/ApiShowEntityStateController.php
@@ -23,6 +23,7 @@ public function update(string $entityKey, mixed $instanceId = null)
return $this->returnCommandResult(
$showPage,
+ $entityKey,
array_merge(
$stateHandler->execute($instanceId, request()->only('value')),
['value' => request('value')],
diff --git a/src/Http/Controllers/Api/Commands/ApiShowInstanceCommandController.php b/src/Http/Controllers/Api/Commands/ApiShowInstanceCommandController.php
index 961522aa2..0cfbc6edf 100644
--- a/src/Http/Controllers/Api/Commands/ApiShowInstanceCommandController.php
+++ b/src/Http/Controllers/Api/Commands/ApiShowInstanceCommandController.php
@@ -37,7 +37,7 @@ public function update(string $entityKey, string $commandKey, mixed $instanceId
$commandHandler = $this->getInstanceCommandHandler($showPage, $commandKey, $instanceId);
$formattedData = $commandHandler->formatAndValidateRequestData((array) request('data'), $instanceId);
- $result = $this->returnCommandResult($showPage, $commandHandler->execute($instanceId, $formattedData));
+ $result = $this->returnCommandResult($showPage, $entityKey, $commandHandler->execute($instanceId, $formattedData));
$this->uploadManager->dispatchJobs($instanceId);
return $result;
diff --git a/src/Http/Controllers/Api/Commands/HandlesCommandResult.php b/src/Http/Controllers/Api/Commands/HandlesCommandResult.php
index 73797334b..aa46dc27f 100644
--- a/src/Http/Controllers/Api/Commands/HandlesCommandResult.php
+++ b/src/Http/Controllers/Api/Commands/HandlesCommandResult.php
@@ -5,6 +5,7 @@
use Code16\Sharp\Dashboard\SharpDashboard;
use Code16\Sharp\EntityList\SharpEntityList;
use Code16\Sharp\Enums\CommandAction;
+use Code16\Sharp\Http\Controllers\HandlesEntityListItems;
use Code16\Sharp\Show\SharpShow;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Storage;
@@ -12,8 +13,11 @@
trait HandlesCommandResult
{
+ use HandlesEntityListItems;
+
protected function returnCommandResult(
SharpEntityList|SharpShow|SharpDashboard $commandContainer,
+ string $entityKey,
array $returnedValue
): StreamedResponse|JsonResponse {
if ($returnedValue['action'] == CommandAction::Download->value) {
@@ -35,9 +39,13 @@ function () use ($returnedValue) {
if ($returnedValue['action'] == CommandAction::Refresh->value && $commandContainer instanceof SharpEntityList) {
// We have to load and build items from ids
- $returnedValue['items'] = $commandContainer
- ->updateQueryParamsWithSpecificIds($returnedValue['items'])
- ->data()['items'];
+ $returnedValue['items'] = $this->addMetaToItems(
+ $commandContainer
+ ->updateQueryParamsWithSpecificIds($returnedValue['items'])
+ ->data()['items'],
+ $entityKey,
+ $commandContainer,
+ );
}
return response()->json($returnedValue);
diff --git a/src/Http/Controllers/EntityListController.php b/src/Http/Controllers/EntityListController.php
index 56fc64dfe..65f005433 100644
--- a/src/Http/Controllers/EntityListController.php
+++ b/src/Http/Controllers/EntityListController.php
@@ -6,13 +6,18 @@
use Code16\Sharp\Data\BreadcrumbData;
use Code16\Sharp\Data\EntityList\EntityListData;
use Code16\Sharp\Data\NotificationData;
+use Code16\Sharp\EntityList\EntityListEntity;
+use Code16\Sharp\EntityList\SharpEntityList;
use Code16\Sharp\Exceptions\SharpInvalidConfigException;
use Code16\Sharp\Utils\Entities\SharpEntityManager;
+use Code16\Sharp\Utils\Entities\ValueObjects\EntityKey;
+use Code16\Sharp\Utils\Icons\IconManager;
use Code16\Sharp\Utils\Menu\SharpMenuManager;
use Inertia\Inertia;
class EntityListController extends SharpProtectedController
{
+ use HandlesEntityListItems;
use HandlesSharpNotificationsInRequest;
public function __construct(
@@ -35,19 +40,23 @@ public function show(string $entityKey)
$data = [
'fields' => $list->fields(),
- 'data' => $listData['items'],
+ 'data' => $this->addMetaToItems($listData['items'], $entityKey, $list),
'meta' => $listData['meta'],
'pageAlert' => $list->pageAlert($listData['items']),
- 'config' => $listConfig,
- 'authorizations' => $this->getAuthorizationsForEntityList(
+ 'config' => [
+ ...$listConfig,
+ 'formCreateUrl' => route('code16.sharp.form.create', [
+ 'parentUri' => sharp()->context()->breadcrumb()->getCurrentPath(),
+ 'entityKey' => $entityKey,
+ ]),
+ ],
+ 'authorizations' => [
+ 'reorder' => $this->sharpAuthorizationManager->isAllowed('reorder', $entityKey),
+ 'create' => $this->sharpAuthorizationManager->isAllowed('create', $entityKey),
+ ],
+ 'entities' => $this->getEntitiesDataForEntityList(
$entityKey,
- $listData['items'],
- $listConfig,
- ),
- 'forms' => $this->getMultiformDataForEntityList(
- $entityKey,
- $listData['items'],
- $listConfig,
+ $list
),
'filterValues' => $list->filterContainer()->getCurrentFilterValuesForFront(request()->all()),
'query' => count(request()->query()) ? request()->query() : null,
@@ -72,54 +81,62 @@ public function show(string $entityKey)
]);
}
- private function getAuthorizationsForEntityList(string $entityKey, array $listItems, array $listConfig): array
- {
- $authorizations = [
- 'view' => [],
- 'reorder' => $this->sharpAuthorizationManager->isAllowed('reorder', $entityKey),
- 'delete' => [],
- 'create' => $this->sharpAuthorizationManager->isAllowed('create', $entityKey),
- ];
-
- // Collect instanceIds from response
- collect($listItems)
- ->pluck($listConfig['instanceIdAttribute'])
- ->each(function ($instanceId) use (&$authorizations, $entityKey) {
- if ($this->sharpAuthorizationManager->isAllowed('view', $entityKey, $instanceId)) {
- $authorizations['view'][] = $instanceId;
- }
- if ($this->sharpAuthorizationManager->isAllowed('delete', $entityKey, $instanceId)) {
- $authorizations['delete'][] = $instanceId;
- }
- });
-
- return $authorizations;
- }
-
- private function getMultiformDataForEntityList(string $entityKey, array $listItems, array $listConfig): ?array
+ private function getEntitiesDataForEntityList(string $entityKey, SharpEntityList $list): ?array
{
- if ($listConfig['multiformAttribute'] === null) {
+ if ($list->getEntityAttribute() === null) {
return null;
}
- if (! $forms = $this->entityManager->entityFor($entityKey)->getMultiforms()) {
+ $forms = $this->entityManager->entityFor($entityKey)->getMultiforms();
+ $entities = $list->getEntities()?->all();
+
+ if (! $forms && ! $entities) {
throw new SharpInvalidConfigException(
- 'The list for the entity ['.$entityKey.'] defines a multiform attribute ['
- .$listConfig['multiformAttribute']
- .'] but the entity is not configured as multiform.'
+ 'The list for the entity ['.$entityKey.'] defines a sub-entity attribute ['
+ .$list->getEntityAttribute()
+ .'] but the entity is has no sub-entities.'
);
}
- return collect($forms)
- ->map(fn ($value, $key) => [
- 'key' => $key,
- 'label' => is_array($value) && count($value) > 1 ? $value[1] : $key,
- 'instances' => collect($listItems)
- ->where($listConfig['multiformAttribute'], $key)
- ->pluck($listConfig['instanceIdAttribute'])
- ->toArray(),
- ])
- ->keyBy('key')
+ if ($forms) {
+ return collect($forms)
+ ->map(fn ($value, $key) => [
+ 'key' => $key,
+ 'entityKey' => EntityKey::multiform(baseKey: $entityKey, multiformKey: $key),
+ 'label' => is_array($value) && count($value) > 1 ? $value[1] : $key,
+ 'icon' => null,
+ 'formCreateUrl' => route('code16.sharp.form.create', [
+ 'parentUri' => sharp()->context()->breadcrumb()->getCurrentPath(),
+ 'entityKey' => EntityKey::multiform(baseKey: $entityKey, multiformKey: $key),
+ ]),
+ ])
+ ->whereNotNull('label')
+ ->values()
+ ->all();
+ }
+
+ return collect($entities)
+ ->map(function (EntityListEntity $listEntity, $key) {
+ $entity = $listEntity->getEntity();
+ $entityKey = $this->entityManager->entityKeyFor($entity);
+
+ if (! $this->sharpAuthorizationManager->isAllowed('create', $entityKey)) {
+ return null;
+ }
+
+ return [
+ 'key' => $key,
+ 'entityKey' => $entityKey,
+ 'label' => $listEntity->getLabel() ?: $entity->getLabelOrFail(),
+ 'icon' => app(IconManager::class)->iconToArray($listEntity->getIcon()),
+ 'formCreateUrl' => route('code16.sharp.form.create', [
+ 'parentUri' => sharp()->context()->breadcrumb()->getCurrentPath(),
+ 'entityKey' => $entityKey,
+ ]),
+ ];
+ })
+ ->filter()
+ ->values()
->all();
}
}
diff --git a/src/Http/Controllers/FormController.php b/src/Http/Controllers/FormController.php
index bf9ef41f8..8428ef0d6 100644
--- a/src/Http/Controllers/FormController.php
+++ b/src/Http/Controllers/FormController.php
@@ -30,7 +30,7 @@ public function create(string $parentUri, EntityKey $entityKey)
{
$entity = $this->entityManager->entityFor($entityKey);
- $form = $entity->getFormOrFail($entityKey->subEntity());
+ $form = $entity->getFormOrFail($entityKey->multiformKey());
if ($form instanceof SharpSingleForm) {
// There is no creation in SingleForms
@@ -47,7 +47,7 @@ public function create(string $parentUri, EntityKey $entityKey)
'form' => FormData::from([
...$this->buildFormData($form, $formData, $entityKey),
'title' => $form->getCreateTitle() ?: trans('sharp::breadcrumb.form.create_entity', [
- 'entity' => $entity->getLabelOrFail($entityKey->subEntity()),
+ 'entity' => $entity->getLabelOrFail($entityKey->multiformKey()),
]),
]),
'breadcrumb' => BreadcrumbData::from([
@@ -72,7 +72,7 @@ public function edit(string $parentUri, EntityKey $entityKey, ?string $instanceI
$instanceId
);
- $form = $entity->getFormOrFail($entityKey->subEntity());
+ $form = $entity->getFormOrFail($entityKey->multiformKey());
abort_if(
(! $instanceId && ! $form instanceof SharpSingleForm)
@@ -94,7 +94,7 @@ public function edit(string $parentUri, EntityKey $entityKey, ?string $instanceI
$titleEntityLabel ??= sharp()
->context()
->breadcrumb()
- ->getParentShowCachedBreadcrumbLabel() ?: $entity->getLabelOrFail($entityKey->subEntity());
+ ->getParentShowCachedBreadcrumbLabel() ?: $entity->getLabelOrFail($entityKey->multiformKey());
return Inertia::render('Form/Form', [
'form' => FormData::from([
@@ -126,7 +126,7 @@ public function update(string $parentUri, EntityKey $entityKey, ?string $instanc
$form = $this->entityManager
->entityFor($entityKey)
- ->getFormOrFail($entityKey->subEntity());
+ ->getFormOrFail($entityKey->multiformKey());
abort_if(
(! $instanceId && ! $form instanceof SharpSingleForm)
@@ -152,7 +152,7 @@ public function store(string $parentUri, EntityKey $entityKey)
{
$form = $this->entityManager
->entityFor($entityKey)
- ->getFormOrFail($entityKey->subEntity());
+ ->getFormOrFail($entityKey->multiformKey());
if ($form instanceof SharpSingleForm) {
// There is no creation in SingleForms
diff --git a/src/Http/Controllers/HandlesEntityListItems.php b/src/Http/Controllers/HandlesEntityListItems.php
new file mode 100644
index 000000000..15d78652d
--- /dev/null
+++ b/src/Http/Controllers/HandlesEntityListItems.php
@@ -0,0 +1,89 @@
+entityFor($entityKey);
+
+ return collect($listItems)
+ ->map(function ($item) use ($entity, $entityKey, $list) {
+ $itemEntityKey = $this->getItemEntityKey($item, $entityKey, $entity, $list);
+ $instanceId = $item[$list->getInstanceIdAttribute()] ?? null;
+
+ return [
+ ...$item,
+ '_meta' => [
+ 'url' => $this->getItemUrl($item, $entityKey, $entity, $list),
+ 'authorizations' => [
+ 'view' => app(SharpAuthorizationManager::class)->isAllowed('view', $itemEntityKey, $instanceId),
+ 'delete' => app(SharpAuthorizationManager::class)->isAllowed('delete', $itemEntityKey, $instanceId),
+ ],
+ ],
+ ];
+ })
+ ->all();
+ }
+
+ private function getItemEntityKey(array $item, string $entityKey, SharpEntity $entity, SharpEntityList $list): string
+ {
+ $itemEntityAttributeValue = $list->getEntityAttribute()
+ ? ($item[$list->getEntityAttribute()] ?? null)
+ : null;
+
+ if ($itemEntityAttributeValue) {
+ if (count($entity->getMultiforms()) > 0) {
+ return EntityKey::multiform(baseKey: $entityKey, multiformKey: $itemEntityAttributeValue);
+ }
+
+ if (! $listEntity = ($list->getEntities()->find($itemEntityAttributeValue))) {
+ throw new SharpInvalidEntityKeyException(
+ sprintf('The sub-entity [%s] for the entity-list [%s] was not found.', $itemEntityAttributeValue, get_class($list))
+ );
+ }
+
+ return $listEntity->getEntityKey();
+ }
+
+ return $entityKey;
+ }
+
+ private function getItemUrl(array $item, string $entityKey, SharpEntity $entity, SharpEntityList $list): ?string
+ {
+ $breadcrumb = sharp()->context()->breadcrumb();
+
+ $itemEntityKey = $this->getItemEntityKey($item, $entityKey, $entity, $list);
+ $instanceId = $item[$list->getInstanceIdAttribute()] ?? null;
+
+ if (! app(SharpAuthorizationManager::class)->isAllowed('view', $itemEntityKey, $instanceId)) {
+ return null;
+ }
+
+ $itemEntity = app(SharpEntityManager::class)->entityFor($itemEntityKey);
+
+ if ($breadcrumb->getCurrentPath()) {
+ return $itemEntity->hasShow()
+ ? route('code16.sharp.show.show', [
+ 'parentUri' => $breadcrumb->getCurrentPath(),
+ 'entityKey' => $itemEntityKey,
+ 'instanceId' => $item[$list->getInstanceIdAttribute()],
+ ])
+ : route('code16.sharp.form.edit', [
+ 'parentUri' => $breadcrumb->getCurrentPath(),
+ 'entityKey' => $itemEntityKey,
+ 'instanceId' => $item[$list->getInstanceIdAttribute()],
+ ]);
+ }
+
+ return null;
+ }
+}
diff --git a/src/Http/Controllers/ShowController.php b/src/Http/Controllers/ShowController.php
index 631305f4a..ddc2d6d57 100644
--- a/src/Http/Controllers/ShowController.php
+++ b/src/Http/Controllers/ShowController.php
@@ -28,6 +28,7 @@ public function show(string $parentUri, EntityKey $entityKey, string $instanceId
sharp_check_ability('view', $entityKey, $instanceId);
$entity = $this->entityManager->entityFor($entityKey);
+
$show = $entity->getShowOrFail();
abort_if($show instanceof SharpSingleShow, 404);
@@ -36,7 +37,7 @@ public function show(string $parentUri, EntityKey $entityKey, string $instanceId
$showData = $show->instance($instanceId);
$payload = ShowData::from([
- 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->subEntity()),
+ 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->multiformKey()),
'config' => $show->showConfig($instanceId),
'fields' => $show->fields(),
'layout' => $show->showLayout(),
diff --git a/src/Http/Controllers/SingleShowController.php b/src/Http/Controllers/SingleShowController.php
index d05d16ff0..8ff06d523 100644
--- a/src/Http/Controllers/SingleShowController.php
+++ b/src/Http/Controllers/SingleShowController.php
@@ -33,7 +33,7 @@ public function show(EntityKey $entityKey)
$showData = $show->instance(null);
$payload = ShowData::from([
- 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->subEntity()),
+ 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->multiformKey()),
'config' => $show->showConfig(null),
'fields' => $show->fields(),
'layout' => $show->showLayout(),
diff --git a/src/Utils/Entities/BaseSharpEntity.php b/src/Utils/Entities/BaseSharpEntity.php
index c01a72476..5dd3efa4d 100644
--- a/src/Utils/Entities/BaseSharpEntity.php
+++ b/src/Utils/Entities/BaseSharpEntity.php
@@ -11,6 +11,7 @@ abstract class BaseSharpEntity
public static string $entityKey;
protected ?string $policy = null;
protected string $label = 'entity';
+ protected ?string $icon = null;
final public function getPolicyOrDefault(): SharpEntityPolicy
{
diff --git a/src/Utils/Entities/SharpEntity.php b/src/Utils/Entities/SharpEntity.php
index 2cf13aca1..e29b842af 100644
--- a/src/Utils/Entities/SharpEntity.php
+++ b/src/Utils/Entities/SharpEntity.php
@@ -42,15 +42,21 @@ final public function hasShow(): bool
return $this->getShow() !== null;
}
- final public function getFormOrFail(?string $subEntity = null): SharpForm
+ final public function getFormOrFail(?string $multiformKey = null): SharpForm
{
- if ($subEntity) {
- if (! $form = ($this->getMultiforms()[$subEntity][0] ?? null)) {
- throw new SharpInvalidEntityKeyException(
- sprintf('The subform for the entity [%s:%s] was not found.', get_class($this), $subEntity)
- );
+ if ($multiformKey) {
+ if (count($this->getMultiforms())) {
+ if (! $form = ($this->getMultiforms()[$multiformKey][0] ?? null)) {
+ throw new SharpInvalidEntityKeyException(
+ sprintf('The subform for the entity [%s:%s] was not found.', get_class($this), $multiformKey)
+ );
+ }
+
+ return instanciate($form);
}
- } elseif (! $form = $this->getForm()) {
+ }
+
+ if (! $form = $this->getForm()) {
throw new SharpInvalidEntityKeyException(
sprintf('The form for the entity [%s] was not found.', get_class($this))
);
@@ -59,15 +65,15 @@ final public function getFormOrFail(?string $subEntity = null): SharpForm
return instanciate($form);
}
- final public function getLabelOrFail(?string $subEntity = null): string
+ final public function getLabelOrFail(?string $multiformKey = null): string
{
- $label = $subEntity
- ? $this->getMultiforms()[$subEntity][1] ?? null
+ $label = $multiformKey
+ ? $this->getMultiforms()[$multiformKey][1] ?? null
: $this->getLabel();
if ($label === null) {
throw new SharpInvalidEntityKeyException(
- sprintf('The label of the subform for the entity [%s:%s] was not found.', get_class($this), $subEntity)
+ sprintf('The label of the subform for the entity [%s:%s] was not found.', get_class($this), $multiformKey)
);
}
@@ -110,6 +116,10 @@ protected function getForm(): ?SharpForm
return $this->form ? app($this->form) : null;
}
+ /**
+ * @deprecated
+ * @see SharpEntityList::configureEntityMap()
+ */
public function getMultiforms(): array
{
return [];
diff --git a/src/Utils/Entities/SharpEntityManager.php b/src/Utils/Entities/SharpEntityManager.php
index 78de98f2d..286b2e128 100644
--- a/src/Utils/Entities/SharpEntityManager.php
+++ b/src/Utils/Entities/SharpEntityManager.php
@@ -4,13 +4,13 @@
use Code16\Sharp\Exceptions\SharpInvalidConfigException;
use Code16\Sharp\Exceptions\SharpInvalidEntityKeyException;
-use Illuminate\Support\Str;
+use Code16\Sharp\Utils\Entities\ValueObjects\EntityKey;
class SharpEntityManager
{
public function entityFor(string $entityKey): SharpEntity|SharpDashboardEntity
{
- $entityKey = Str::before($entityKey, ':');
+ $entityKey = (new EntityKey($entityKey))->baseKey();
if (count(sharp()->config()->get('entities')) > 0) {
$entityClass = sharp()->config()->get('entities.'.$entityKey);
diff --git a/src/Utils/Entities/ValueObjects/EntityKey.php b/src/Utils/Entities/ValueObjects/EntityKey.php
index bd9978b1c..035e232a8 100644
--- a/src/Utils/Entities/ValueObjects/EntityKey.php
+++ b/src/Utils/Entities/ValueObjects/EntityKey.php
@@ -15,6 +15,11 @@ public function __construct(
protected ?string $key = null
) {}
+ public static function multiform(string $baseKey, ?string $multiformKey): static
+ {
+ return new static($multiformKey ? "$baseKey:$multiformKey" : $baseKey);
+ }
+
public function baseKey(): string
{
return str_contains($this->key, ':')
@@ -22,7 +27,7 @@ public function baseKey(): string
: $this->key;
}
- public function subEntity(): ?string
+ public function multiformKey(): ?string
{
return str_contains($this->key, ':')
? Str::after($this->key, ':')
diff --git a/src/routes/api.php b/src/routes/api.php
index 3010b6f34..9d025f701 100644
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -29,10 +29,10 @@
Route::post('/dashboard/{dashboardKey}/command/{commandKey}', [ApiDashboardCommandController::class, 'update'])
->name('code16.sharp.api.dashboard.command');
- Route::get('/list/{entityKey}/create', [ApiEntityListQuickCreationCommandController::class, 'create'])
+ Route::get('/list/{entityKey}/form/{formEntityKey}/create', [ApiEntityListQuickCreationCommandController::class, 'create'])
->name('code16.sharp.api.list.command.quick-creation-form.create');
- Route::post('/list/{entityKey}/create', [ApiEntityListQuickCreationCommandController::class, 'store'])
+ Route::post('/list/{entityKey}/form/{formEntityKey}/create', [ApiEntityListQuickCreationCommandController::class, 'store'])
->name('code16.sharp.api.list.command.quick-creation-form.store');
// EEL
diff --git a/src/sharp_helper.php b/src/sharp_helper.php
index 47e30108a..c82597ac7 100644
--- a/src/sharp_helper.php
+++ b/src/sharp_helper.php
@@ -1,7 +1,5 @@
isAllowed($ability, Str::before($entityKey, ':'), $instanceId);
+ ->isAllowed($ability, $entityKey, $instanceId);
}
function sharp_check_ability(string $ability, string $entityKey, ?string $instanceId = null)
{
app(Code16\Sharp\Auth\SharpAuthorizationManager::class)
- ->check($ability, Str::before($entityKey, ':'), $instanceId);
+ ->check($ability, $entityKey, $instanceId);
}
diff --git a/tests/Fixtures/Entities/PersonChemistEntity.php b/tests/Fixtures/Entities/PersonChemistEntity.php
new file mode 100644
index 000000000..525a9db8c
--- /dev/null
+++ b/tests/Fixtures/Entities/PersonChemistEntity.php
@@ -0,0 +1,9 @@
+config()->declareEntity(PersonEntity::class);
@@ -133,14 +135,21 @@ public function getListData(): array|Arrayable
}
});
- $this->getJson('/sharp/api/list/person')
+ $this->getJson('/sharp/api/list/person', headers: [
+ SharpBreadcrumb::CURRENT_PAGE_URL_HEADER => url('/sharp/s-list/person'),
+ ])
->assertOk()
- ->assertJsonFragment([
- 'data' => [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Niels Bohr'],
- ],
- ]);
+ ->assertJson(fn (AssertableJson $json) => $json
+ ->has('data.0', fn (AssertableJson $json) => $json->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('data.1', fn (AssertableJson $json) => $json->where('id', 2)
+ ->where('name', 'Niels Bohr')
+ ->etc()
+ )
+ ->etc()
+ );
});
it('gets paginated data if wanted as JSON in an EEL case', function () {
@@ -157,25 +166,29 @@ public function getListData(): array|Arrayable
}
});
- $metaJson = $this->getJson('/sharp/api/list/person')
+ $this->getJson('/sharp/api/list/person', headers: [
+ SharpBreadcrumb::CURRENT_PAGE_URL_HEADER => url('/sharp/s-list/person'),
+ ])
->assertOk()
- ->assertJsonFragment([
- 'data' => [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Niels Bohr'],
- ],
- ])
- ->json('meta');
-
- expect($metaJson)
- ->toMatchArray([
- 'current_page' => 1,
- 'first_page_url' => '/?page=1',
- 'last_page_url' => '/?page=10',
- 'from' => 1,
- 'to' => 2,
- 'last_page' => 10,
- 'per_page' => 2,
- 'total' => 20,
- ]);
+ ->assertJson(fn (AssertableJson $json) => $json
+ ->has('data.0', fn (AssertableJson $json) => $json->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('data.1', fn (AssertableJson $json) => $json->where('id', 2)
+ ->where('name', 'Niels Bohr')
+ ->etc()
+ )
+ ->has('meta', fn (AssertableJson $json) => $json->where('current_page', 1)
+ ->where('per_page', 2)
+ ->where('total', 20)
+ ->where('from', 1)
+ ->where('to', 2)
+ ->where('last_page', 10)
+ ->where('first_page_url', '/?page=1')
+ ->where('last_page_url', '/?page=10')
+ ->etc()
+ )
+ ->etc()
+ );
});
diff --git a/tests/Http/Api/Commands/ApiEntityListQuickCreationCommandControllerTest.php b/tests/Http/Api/Commands/ApiEntityListQuickCreationCommandControllerTest.php
index 1e6825f3b..8359cf5d9 100644
--- a/tests/Http/Api/Commands/ApiEntityListQuickCreationCommandControllerTest.php
+++ b/tests/Http/Api/Commands/ApiEntityListQuickCreationCommandControllerTest.php
@@ -34,7 +34,7 @@ public function buildFormFields(FieldsContainer $formFields): void
$this
->getJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
)
->assertOk()
->assertJson([
@@ -69,7 +69,7 @@ public function buildFormFields(FieldsContainer $formFields): void
$this
->getJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
)
->assertOk()
->assertJsonCount(1, 'fields');
@@ -83,7 +83,7 @@ public function buildListConfig(): void {}
$this
->getJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
)
->assertStatus(403);
});
@@ -118,7 +118,7 @@ public function update($id, array $data)
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
['data' => ['name' => 'Marie Curie', 'job' => 'Scientist']],
)
->assertOk()
@@ -143,7 +143,7 @@ public function update($id, array $data) {}
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
['data' => []],
)
->assertOk()
@@ -178,7 +178,7 @@ public function update($id, array $data)
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
['data' => []],
[
SharpBreadcrumb::CURRENT_PAGE_URL_HEADER => url('/sharp/s-list/person'),
@@ -217,7 +217,7 @@ public function update($id, array $data)
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
['data' => ['name' => '']],
)
->assertStatus(422)
@@ -250,7 +250,7 @@ public function update($id, array $data)
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['person']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['person', 'person']),
['data' => ['name' => 'Marie Curie']],
)
->assertOk()
@@ -298,7 +298,7 @@ public function update($id, array $data)
// Simulate a post of the colleague quick creation command from an EEL
$this
->postJson(
- route('code16.sharp.api.list.command.quick-creation-form.create', ['colleague']),
+ route('code16.sharp.api.list.command.quick-creation-form.create', ['colleague', 'colleague']),
['data' => ['name' => 'Marie Curie']],
)
->assertOk()
diff --git a/tests/Http/Auth/AuthorizationsTest.php b/tests/Http/Auth/AuthorizationsTest.php
index babcf7288..a901221e6 100644
--- a/tests/Http/Auth/AuthorizationsTest.php
+++ b/tests/Http/Auth/AuthorizationsTest.php
@@ -11,7 +11,7 @@
use Code16\Sharp\Utils\Entities\SharpEntityManager;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Gate;
-use Inertia\Testing\AssertableInertia as Assert;
+use Illuminate\Testing\Fluent\AssertableJson;
beforeEach(function () {
login();
@@ -73,7 +73,7 @@
$this
->get('/sharp/s-list/person/s-form/person')
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('form.authorizations', [
'delete' => false,
'update' => false,
@@ -83,7 +83,7 @@
);
$this->get('/sharp/s-list/person/s-show/person/1')
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('show.authorizations', [
'delete' => false,
'update' => false,
@@ -110,7 +110,7 @@ public function findSingle(): array
$this
->get('/sharp/s-show/single_person')
->assertOk()
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('show.authorizations', [
'delete' => false,
'update' => true,
@@ -138,12 +138,18 @@ public function getListData(): array|Arrayable
$this
->get('/sharp/s-list/person')
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('entityList.authorizations', [
- 'delete' => [],
'reorder' => true,
'create' => true,
- 'view' => [1, 2],
+ ])
+ ->where('entityList.data.0._meta.authorizations', [
+ 'view' => true,
+ 'delete' => false,
+ ])
+ ->where('entityList.data.1._meta.authorizations', [
+ 'view' => true,
+ 'delete' => false,
])
);
});
@@ -163,7 +169,7 @@ public function getListData(): array|Arrayable
// Create (no instanceId, only create is allowed)
$this
->get('/sharp/s-list/person/s-form/person')
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('form.authorizations', [
'delete' => false,
'update' => false,
@@ -175,7 +181,7 @@ public function getListData(): array|Arrayable
// Edit
$this
->get('/sharp/s-list/person/s-form/person/1')
- ->assertInertia(fn (Assert $page) => $page
+ ->assertInertia(fn (AssertableJson $json) => $json
->where('form.authorizations', [
'delete' => true,
'update' => true,
@@ -187,26 +193,42 @@ public function getListData(): array|Arrayable
// EL (inertia)
$this
->get('/sharp/s-list/person')
- ->assertInertia(fn (Assert $page) => $page
- ->where('entityList.authorizations', [
- 'delete' => [1, 2],
- 'reorder' => true,
- 'create' => true,
- 'view' => [1, 2],
- ])
+ ->assertInertia(fn (AssertableJson $json) => $json
+ ->has('entityList', fn (AssertableJson $json) => $json
+ ->where('authorizations', [
+ 'reorder' => true,
+ 'create' => true,
+ ])
+ ->where('data.0._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
+ ->where('data.1._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
+ ->etc()
+ )
);
// EEL (json)
$this
->getJson('/sharp/api/list/person')
- ->assertJsonFragment([
- 'authorizations' => [
- 'delete' => [1, 2],
+ ->assertJson(fn (AssertableJson $json) => $json
+ ->where('authorizations', [
'reorder' => true,
'create' => true,
- 'view' => [1, 2],
- ],
- ]);
+ ])
+ ->where('data.0._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
+ ->where('data.1._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
+ ->etc()
+ );
});
it('checks the main entity prohibited actions in case of a sub entity', function () {
diff --git a/tests/Http/Auth/PolicyAuthorizationsTest.php b/tests/Http/Auth/PolicyAuthorizationsTest.php
index 987f11cb3..a085bcd89 100644
--- a/tests/Http/Auth/PolicyAuthorizationsTest.php
+++ b/tests/Http/Auth/PolicyAuthorizationsTest.php
@@ -128,10 +128,16 @@ public function delete($user, $instanceId): bool
->get('/sharp/s-list/person')
->assertInertia(fn (Assert $page) => $page
->where('entityList.authorizations', [
- 'delete' => [1],
'reorder' => true,
'create' => true,
- 'view' => [1, 2],
+ ])
+ ->where('entityList.data.0._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
+ ->where('entityList.data.1._meta.authorizations', [
+ 'view' => true,
+ 'delete' => false,
])
);
});
diff --git a/tests/Http/EntityListControllerTest.php b/tests/Http/EntityListControllerTest.php
index 522ccb6ea..f2e641aa4 100644
--- a/tests/Http/EntityListControllerTest.php
+++ b/tests/Http/EntityListControllerTest.php
@@ -1,12 +1,15 @@
get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Niels Bohr'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('entityList.data.1', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Niels Bohr')
+ ->etc()
+ )
+ ->count('entityList.data', 2)
);
});
@@ -60,10 +70,17 @@ public function getListData(): array|Arrayable
$this->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Niels Bohr'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('entityList.data.1', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Niels Bohr')
+ ->etc()
+ )
+ ->count('entityList.data', 2)
->has('entityList.meta', fn (Assert $name) => $name
->where('current_page', 1)
->where('from', 1)
@@ -108,10 +125,17 @@ public function buildListConfig(): void
$this->get('/sharp/s-list/person?search=Curie')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 3, 'name' => 'Pierre Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('entityList.data.1', fn (Assert $json) => $json
+ ->where('id', 3)
+ ->where('name', 'Pierre Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 2)
);
});
@@ -241,12 +265,18 @@ public function delete($user, $instanceId): bool
$this->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->has('entityList.authorizations', fn (Assert $config) => $config
- ->where('create', true)
- ->where('view', [1, 2])
- ->where('reorder', true)
- ->where('delete', [2])
- )
+ ->where('entityList.authorizations', [
+ 'create' => true,
+ 'reorder' => true,
+ ])
+ ->where('entityList.data.0._meta.authorizations', [
+ 'view' => true,
+ 'delete' => false,
+ ])
+ ->where('entityList.data.1._meta.authorizations', [
+ 'view' => true,
+ 'delete' => true,
+ ])
);
});
@@ -278,15 +308,66 @@ public function buildListConfig(): void
$this->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->has('entityList.forms', 2)
- ->has('entityList.forms.yes', fn (Assert $config) => $config
+ ->has('entityList.entities', 2)
+ ->has('entityList.entities.0', fn (Assert $config) => $config
+ ->where('key', 'yes')
+ ->where('entityKey', 'person:yes')
->where('label', 'With Nobel prize')
- ->where('instances', [1])
+ // ->where('instances', [1])
->etc()
)
- ->has('entityList.forms.nope', fn (Assert $config) => $config
+ ->has('entityList.entities.1', fn (Assert $config) => $config
+ ->where('key', 'nope')
+ ->where('entityKey', 'person:nope')
->where('label', 'No Nobel prize')
- ->where('instances', [2])
+ // ->where('instances', [2])
+ ->etc()
+ )
+ );
+});
+
+it('get entities if configured', function () {
+ $this->withoutExceptionHandling();
+
+ sharp()->config()->declareEntity(PersonChemistEntity::class);
+ sharp()->config()->declareEntity(PersonPhysicistEntity::class);
+
+ fakeListFor('person', new class() extends PersonList
+ {
+ public function getListData(): array|Arrayable
+ {
+ return [
+ ['id' => 1, 'name' => 'Marie Curie', 'job' => 'chemist'],
+ ['id' => 2, 'name' => 'Rosalind Franklin', 'job' => 'physicist'],
+ ];
+ }
+
+ public function buildListConfig(): void
+ {
+ $this->configureEntityMap(
+ attribute: 'job',
+ entities: EntityListEntities::make()
+ ->addEntity('chemist', PersonChemistEntity::class, icon: 'testicon-car')
+ ->addEntity('physicist', PersonPhysicistEntity::class),
+ );
+ }
+ });
+
+ $this->get('/sharp/s-list/person')
+ ->assertOk()
+ ->assertInertia(fn (Assert $page) => $page
+ ->has('entityList.entities', 2)
+ ->has('entityList.entities.0', fn (Assert $config) => $config
+ ->where('key', 'chemist')
+ ->where('entityKey', 'person-chemist')
+ ->where('label', 'Chemist')
+ ->where('icon.name', 'testicon-car')
+ ->etc()
+ )
+ ->has('entityList.entities.1', fn (Assert $config) => $config
+ ->where('key', 'physicist')
+ ->where('entityKey', 'person-physicist')
+ ->where('label', 'Physicist')
->etc()
)
);
diff --git a/tests/Http/FiltersInRequestTest.php b/tests/Http/FiltersInRequestTest.php
index c172c5bd5..3bb05cada 100644
--- a/tests/Http/FiltersInRequestTest.php
+++ b/tests/Http/FiltersInRequestTest.php
@@ -53,9 +53,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person?filter_job=physicist')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
});
@@ -103,9 +106,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
});
@@ -149,10 +155,17 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person?filter_job=physicist,physician')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Louis Pasteur'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('entityList.data.1', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Louis Pasteur')
+ ->etc()
+ )
+ ->count('entityList.data', 2)
);
});
@@ -213,9 +226,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
// Third call: should use QS instead of session
@@ -223,9 +239,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person?filter_job=physician')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 2, 'name' => 'Louis Pasteur'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Louis Pasteur')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
// reset retained filter value
@@ -242,10 +261,17 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ['id' => 2, 'name' => 'Louis Pasteur'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->has('entityList.data.1', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Louis Pasteur')
+ ->etc()
+ )
+ ->count('entityList.data', 2)
);
});
@@ -342,9 +368,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
$this
@@ -361,9 +390,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person?filter_job=physician')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 2, 'name' => 'Louis Pasteur'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Louis Pasteur')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
// Third call: no filter, use retained value
@@ -371,9 +403,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 2, 'name' => 'Louis Pasteur'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 2)
+ ->where('name', 'Louis Pasteur')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
$this
@@ -390,9 +425,12 @@ public function getListData(): array|Arrayable
->get('/sharp/s-list/person')
->assertOk()
->assertInertia(fn (Assert $page) => $page
- ->where('entityList.data', [
- ['id' => 1, 'name' => 'Marie Curie'],
- ])
+ ->has('entityList.data.0', fn (Assert $json) => $json
+ ->where('id', 1)
+ ->where('name', 'Marie Curie')
+ ->etc()
+ )
+ ->count('entityList.data', 1)
);
});
diff --git a/tests/Unit/EntityList/SharpEntityListTest.php b/tests/Unit/EntityList/SharpEntityListTest.php
index 4a3e8776b..ffb18eda5 100644
--- a/tests/Unit/EntityList/SharpEntityListTest.php
+++ b/tests/Unit/EntityList/SharpEntityListTest.php
@@ -248,7 +248,7 @@ public function buildListConfig(): void
'reorderable' => false,
'hasShowPage' => false,
'instanceIdAttribute' => 'id',
- 'multiformAttribute' => null,
+ 'entityAttribute' => null,
'defaultSort' => 'name',
'defaultSortDir' => 'asc',
'deleteHidden' => false,