From 1deb4ad55514fc474be82858a7eadda051a57052 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Thu, 27 Feb 2025 10:09:47 +0100 Subject: [PATCH 1/4] Handle dynamic label in entities --- demo/app/Sharp/Entities/ProfileEntity.php | 5 ++++- demo/resources/lang/en.json | 3 +++ demo/resources/lang/fr.json | 3 +++ docs/guide/entity-class.md | 6 +++++- src/Utils/Entities/SharpEntity.php | 7 ++++++- 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 demo/resources/lang/en.json create mode 100644 demo/resources/lang/fr.json diff --git a/demo/app/Sharp/Entities/ProfileEntity.php b/demo/app/Sharp/Entities/ProfileEntity.php index 2fb331a2f..d0d0258a3 100644 --- a/demo/app/Sharp/Entities/ProfileEntity.php +++ b/demo/app/Sharp/Entities/ProfileEntity.php @@ -11,5 +11,8 @@ class ProfileEntity extends SharpEntity protected bool $isSingle = true; protected ?string $show = ProfileSingleShow::class; protected ?string $form = ProfileSingleForm::class; - protected string $label = 'My profile'; + protected function label(): ?string + { + return __('profile'); + } } diff --git a/demo/resources/lang/en.json b/demo/resources/lang/en.json new file mode 100644 index 000000000..68956b3cd --- /dev/null +++ b/demo/resources/lang/en.json @@ -0,0 +1,3 @@ +{ + "profile": "Profile" +} \ No newline at end of file diff --git a/demo/resources/lang/fr.json b/demo/resources/lang/fr.json new file mode 100644 index 000000000..17e2097dc --- /dev/null +++ b/demo/resources/lang/fr.json @@ -0,0 +1,3 @@ +{ + "profile": "Profil" +} \ No newline at end of file diff --git a/docs/guide/entity-class.md b/docs/guide/entity-class.md index 82b2d9825..8663689b7 100644 --- a/docs/guide/entity-class.md +++ b/docs/guide/entity-class.md @@ -28,12 +28,16 @@ class ProductEntity extends SharpEntity protected ?string $list = ProductList::class; protected ?string $show = ProductShow::class; protected ?string $form = ProductForm::class; + protected string $label = 'Product'; + // or + protected function label(): ?string { return __('Product'); } ``` Here is the full list: - `$list`, `$show`, `$form` and `$policy` may be set to a full classname of a corresponding type. The following sections of this documentation describe all this in detail, allowing you to build your Sharp backend. -- `string $label` is used in the breadcrumb, as a default ([see the breadcrumb documentation for more on this](sharp-breadcrumb.md)). You can simply put your entity name here. +- `string $label` is used in the breadcrumb, as a default ([see the breadcrumb documentation for more on this](sharp-breadcrumb.md)). You can simply put your entity name here. \ +You can also define a `protected function label(): ?string` method in the entity class to return a dynamic label (i.e: a translated label). - `bool $isSingle` must be set only if you are dealing [with a single show](single-show.md) - and finally `array $prohibitedActions` can be used to set globally prohibited actions on the entity, [as documented here](entity-authorizations.md). diff --git a/src/Utils/Entities/SharpEntity.php b/src/Utils/Entities/SharpEntity.php index 0b787bc40..0933521f0 100644 --- a/src/Utils/Entities/SharpEntity.php +++ b/src/Utils/Entities/SharpEntity.php @@ -61,9 +61,14 @@ final public function isSingle(): bool return $this->isSingle; } + protected function label(): ?string + { + return null; + } + final public function getLabel(?string $subEntity = null): string { - return $subEntity ? $this->getMultiforms()[$subEntity][1] : $this->label; + return $subEntity ? $this->getMultiforms()[$subEntity][1] : ($this->label() ?? $this->label); } protected function getList(): ?SharpEntityList From f861e1692914c7b9e5a67df9222a7dea91457165 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Thu, 27 Feb 2025 10:22:55 +0100 Subject: [PATCH 2/4] Add test --- .../Entities/DynamicLabelPersonEntity.php | 22 +++++++++++++++++ tests/Http/BreadcrumbTest.php | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/Fixtures/Entities/DynamicLabelPersonEntity.php diff --git a/tests/Fixtures/Entities/DynamicLabelPersonEntity.php b/tests/Fixtures/Entities/DynamicLabelPersonEntity.php new file mode 100644 index 000000000..579ec9a05 --- /dev/null +++ b/tests/Fixtures/Entities/DynamicLabelPersonEntity.php @@ -0,0 +1,22 @@ +where('show.data.name', ['text' => 'Marie Curie']) ); }); + +it("handles dynamic label in entity", function() { + sharp()->config() + ->displayBreadcrumb() + ->addEntity('dynamic_person', DynamicLabelPersonEntity::class); + + fakeFormFor('dynamic_person', new class() extends PersonForm {}); + + $this + ->get( + route('code16.sharp.form.create', [ + 'parentUri' => 's-list/dynamic_person/', + 'dynamic_person', + 1, + ]) + ) + ->assertOk() + ->assertInertia(fn (Assert $page) => $page + ->where('form.title', 'New “'.__('dynamic_label').'”') + ->where('breadcrumb.items.1.documentTitleLabel', 'New “'.__('dynamic_label').'”') + ); +}); From 7cc2e4514a4bcf9d9e47b41918060ee3a8893852 Mon Sep 17 00:00:00 2001 From: philippe Date: Thu, 27 Feb 2025 11:39:03 +0100 Subject: [PATCH 3/4] Allow to override SharpEntity::getLabel() --- demo/app/Sharp/Entities/ProfileEntity.php | 3 ++- docs/guide/entity-class.md | 12 +++++----- src/Http/Context/SharpBreadcrumb.php | 2 +- ...tityListQuickCreationCommandController.php | 2 +- src/Http/Controllers/FormController.php | 8 ++++--- src/Http/Controllers/ShowController.php | 2 +- src/Http/Controllers/SingleShowController.php | 2 +- src/Utils/Entities/BaseSharpEntity.php | 2 +- src/Utils/Entities/SharpDashboardEntity.php | 2 +- src/Utils/Entities/SharpEntity.php | 22 +++++++++++++------ .../Entities/DynamicLabelPersonEntity.php | 22 ------------------- .../Entities/PersonWithDynamicLabelEntity.php | 11 ++++++++++ tests/Http/BreadcrumbTest.php | 22 ++++++++----------- 13 files changed, 53 insertions(+), 59 deletions(-) delete mode 100644 tests/Fixtures/Entities/DynamicLabelPersonEntity.php create mode 100644 tests/Fixtures/Entities/PersonWithDynamicLabelEntity.php diff --git a/demo/app/Sharp/Entities/ProfileEntity.php b/demo/app/Sharp/Entities/ProfileEntity.php index d0d0258a3..d6d040798 100644 --- a/demo/app/Sharp/Entities/ProfileEntity.php +++ b/demo/app/Sharp/Entities/ProfileEntity.php @@ -11,7 +11,8 @@ class ProfileEntity extends SharpEntity protected bool $isSingle = true; protected ?string $show = ProfileSingleShow::class; protected ?string $form = ProfileSingleForm::class; - protected function label(): ?string + + protected function getLabel(): string { return __('profile'); } diff --git a/docs/guide/entity-class.md b/docs/guide/entity-class.md index 8663689b7..36bddb821 100644 --- a/docs/guide/entity-class.md +++ b/docs/guide/entity-class.md @@ -25,19 +25,16 @@ The class must extend `Code16\Sharp\Entities\SharpEntity`. The easiest way to de ```php class ProductEntity extends SharpEntity { + protected string $label = 'Product'; protected ?string $list = ProductList::class; protected ?string $show = ProductShow::class; protected ?string $form = ProductForm::class; - - protected string $label = 'Product'; - // or - protected function label(): ?string { return __('Product'); } +} ``` Here is the full list: - `$list`, `$show`, `$form` and `$policy` may be set to a full classname of a corresponding type. The following sections of this documentation describe all this in detail, allowing you to build your Sharp backend. -- `string $label` is used in the breadcrumb, as a default ([see the breadcrumb documentation for more on this](sharp-breadcrumb.md)). You can simply put your entity name here. \ -You can also define a `protected function label(): ?string` method in the entity class to return a dynamic label (i.e: a translated label). +- `string $label` is used in the breadcrumb, as a default ([see the breadcrumb documentation for more on this](sharp-breadcrumb.md)). You can simply put your entity name here. - `bool $isSingle` must be set only if you are dealing [with a single show](single-show.md) - and finally `array $prohibitedActions` can be used to set globally prohibited actions on the entity, [as documented here](entity-authorizations.md). @@ -58,6 +55,7 @@ class SalesDashboardEntity extends SharpDashboardEntity If you need more control, you can override these instead of the attributes: ```php +protected function getLabel(): string {} protected function getList(): ?string {} protected function getShow(): ?string {} protected function getForm(): ?string {} @@ -69,7 +67,7 @@ The last one, `getPolicy()`, allows you to return a `SharpEntityPolicy` implemen ```php class MyEntity extends SharpEntity { - // [...] + // ... protected function getPolicy(): string|SharpEntityPolicy|null { diff --git a/src/Http/Context/SharpBreadcrumb.php b/src/Http/Context/SharpBreadcrumb.php index 32bd76785..f0dfd7602 100644 --- a/src/Http/Context/SharpBreadcrumb.php +++ b/src/Http/Context/SharpBreadcrumb.php @@ -241,7 +241,7 @@ private function getEntityLabelForInstance(BreadcrumbItem $item, bool $isLeaf): return app(SharpEntityManager::class) ->entityFor($item->key) - ->getLabel((new EntityKey($item->key))->subEntity()); + ->getLabelOrFail((new EntityKey($item->key))->subEntity()); } private function isSameEntityKeys(string $key1, string $key2, bool $compareBaseEntities): bool diff --git a/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php b/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php index 13d168f79..ed31d995b 100644 --- a/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php +++ b/src/Http/Controllers/Api/Commands/ApiEntityListQuickCreationCommandController.php @@ -36,7 +36,7 @@ public function create(EntityKey $entityKey) ->setEntityKey($entityKey) ->setFormInstance($form) ->setTitle(__('sharp::breadcrumb.form.create_entity', [ - 'entity' => $entity->getLabel($entityKey->subEntity()), + 'entity' => $entity->getLabelOrFail($entityKey->subEntity()), ])); $quickCreationHandler->buildCommandConfig(); diff --git a/src/Http/Controllers/FormController.php b/src/Http/Controllers/FormController.php index c7997a0ff..6d82c0faa 100644 --- a/src/Http/Controllers/FormController.php +++ b/src/Http/Controllers/FormController.php @@ -46,7 +46,7 @@ public function create(string $parentUri, EntityKey $entityKey) 'form' => FormData::from([ ...$data, 'title' => $form->getCreateTitle() ?: trans('sharp::breadcrumb.form.create_entity', [ - 'entity' => $entity->getLabel($entityKey->subEntity()), + 'entity' => $entity->getLabelOrFail($entityKey->subEntity()), ]), ]), 'breadcrumb' => BreadcrumbData::from([ @@ -86,8 +86,10 @@ public function edit(string $parentUri, EntityKey $entityKey, ?string $instanceI 'form' => FormData::from([ ...$data, 'title' => $form->getEditTitle() ?: trans('sharp::breadcrumb.form.edit_entity', [ - 'entity' => sharp()->context()->breadcrumb()->getParentShowCachedBreadcrumbLabel() - ?: $entity->getLabel($entityKey->subEntity()), + 'entity' => sharp() + ->context() + ->breadcrumb() + ->getParentShowCachedBreadcrumbLabel() ?: $entity->getLabelOrFail($entityKey->subEntity()), ]), ]), 'breadcrumb' => BreadcrumbData::from([ diff --git a/src/Http/Controllers/ShowController.php b/src/Http/Controllers/ShowController.php index d5444582c..1094aec29 100644 --- a/src/Http/Controllers/ShowController.php +++ b/src/Http/Controllers/ShowController.php @@ -36,7 +36,7 @@ public function show(string $parentUri, EntityKey $entityKey, string $instanceId $showData = $show->instance($instanceId); $payload = ShowData::from([ - 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabel($entityKey->subEntity()), + 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->subEntity()), '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 4418c2aad..6fa51f38f 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->getLabel($entityKey->subEntity()), + 'title' => $showData[$show->titleAttribute()] ?? $entity->getLabelOrFail($entityKey->subEntity()), '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 c07149f48..f9b6e5d76 100644 --- a/src/Utils/Entities/BaseSharpEntity.php +++ b/src/Utils/Entities/BaseSharpEntity.php @@ -32,7 +32,7 @@ final public function getPolicyOrDefault(): SharpEntityPolicy return $policy; } - abstract public function getLabel(): string; + abstract protected function getLabel(): string; final public function isDashboard(): bool { diff --git a/src/Utils/Entities/SharpDashboardEntity.php b/src/Utils/Entities/SharpDashboardEntity.php index 2a1216252..f91d5757c 100644 --- a/src/Utils/Entities/SharpDashboardEntity.php +++ b/src/Utils/Entities/SharpDashboardEntity.php @@ -24,7 +24,7 @@ final public function hasView(): bool return $this->getView() !== null; } - final public function getLabel(): string + protected function getLabel(): string { return $this->label; } diff --git a/src/Utils/Entities/SharpEntity.php b/src/Utils/Entities/SharpEntity.php index 0933521f0..d9a23a183 100644 --- a/src/Utils/Entities/SharpEntity.php +++ b/src/Utils/Entities/SharpEntity.php @@ -51,6 +51,19 @@ final public function getFormOrFail(?string $subEntity = null): SharpForm return $form instanceof SharpForm ? $form : app($form); } + final public function getLabelOrFail(?string $subEntity = null): string + { + $label = $subEntity + ? $this->getMultiforms()[$subEntity][1] ?? null + : $this->getLabel(); + + if ($label === null) { + throw new SharpInvalidEntityKeyException("The label of the subform for the entity [{$this->entityKey}:{$subEntity}] was not found."); + } + + return $label; + } + final public function isActionProhibited(string $action): bool { return in_array($action, $this->prohibitedActions); @@ -61,14 +74,9 @@ final public function isSingle(): bool return $this->isSingle; } - protected function label(): ?string - { - return null; - } - - final public function getLabel(?string $subEntity = null): string + protected function getLabel(): string { - return $subEntity ? $this->getMultiforms()[$subEntity][1] : ($this->label() ?? $this->label); + return $this->label; } protected function getList(): ?SharpEntityList diff --git a/tests/Fixtures/Entities/DynamicLabelPersonEntity.php b/tests/Fixtures/Entities/DynamicLabelPersonEntity.php deleted file mode 100644 index 579ec9a05..000000000 --- a/tests/Fixtures/Entities/DynamicLabelPersonEntity.php +++ /dev/null @@ -1,22 +0,0 @@ -format('Ymd'); + } +} diff --git a/tests/Http/BreadcrumbTest.php b/tests/Http/BreadcrumbTest.php index 581e7fccb..59ed5ee5b 100644 --- a/tests/Http/BreadcrumbTest.php +++ b/tests/Http/BreadcrumbTest.php @@ -1,9 +1,8 @@ config() - ->displayBreadcrumb() - ->addEntity('dynamic_person', DynamicLabelPersonEntity::class); - - fakeFormFor('dynamic_person', new class() extends PersonForm {}); +it('handles dynamic label in entity', function () { + sharp() + ->config() + ->addEntity('person', PersonWithDynamicLabelEntity::class); $this ->get( route('code16.sharp.form.create', [ - 'parentUri' => 's-list/dynamic_person/', - 'dynamic_person', - 1, + 'parentUri' => 's-list/person/', + 'entityKey' => 'person', ]) ) ->assertOk() ->assertInertia(fn (Assert $page) => $page - ->where('form.title', 'New “'.__('dynamic_label').'”') - ->where('breadcrumb.items.1.documentTitleLabel', 'New “'.__('dynamic_label').'”') + ->where('form.title', 'New “'.now()->format('Ymd').'”') + ->where('breadcrumb.items.1.documentTitleLabel', 'New “'.now()->format('Ymd').'”') ); }); From 7cf442d09b71e004671021d2db79205339e668fb Mon Sep 17 00:00:00 2001 From: philippe Date: Thu, 27 Feb 2025 16:33:49 +0100 Subject: [PATCH 4/4] Remove bizarre test --- .../Entities/PersonWithDynamicLabelEntity.php | 11 ----------- tests/Http/BreadcrumbTest.php | 19 ------------------- 2 files changed, 30 deletions(-) delete mode 100644 tests/Fixtures/Entities/PersonWithDynamicLabelEntity.php diff --git a/tests/Fixtures/Entities/PersonWithDynamicLabelEntity.php b/tests/Fixtures/Entities/PersonWithDynamicLabelEntity.php deleted file mode 100644 index 0f46623b0..000000000 --- a/tests/Fixtures/Entities/PersonWithDynamicLabelEntity.php +++ /dev/null @@ -1,11 +0,0 @@ -format('Ymd'); - } -} diff --git a/tests/Http/BreadcrumbTest.php b/tests/Http/BreadcrumbTest.php index 59ed5ee5b..17d0514a2 100644 --- a/tests/Http/BreadcrumbTest.php +++ b/tests/Http/BreadcrumbTest.php @@ -133,22 +133,3 @@ public function find($id): array ->where('show.data.name', ['text' => 'Marie Curie']) ); }); - -it('handles dynamic label in entity', function () { - sharp() - ->config() - ->addEntity('person', PersonWithDynamicLabelEntity::class); - - $this - ->get( - route('code16.sharp.form.create', [ - 'parentUri' => 's-list/person/', - 'entityKey' => 'person', - ]) - ) - ->assertOk() - ->assertInertia(fn (Assert $page) => $page - ->where('form.title', 'New “'.now()->format('Ymd').'”') - ->where('breadcrumb.items.1.documentTitleLabel', 'New “'.now()->format('Ymd').'”') - ); -});