diff --git a/CHANGELOG.md b/CHANGELOG.md index fc173a4f5..f6a93b24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## 0.11 +### Added +- References can also be added to an Entity, not only to an Entity Attribute + ## 0.10.1 ### Added - Option to display attributes in _Data Model Editor_ in groups @@ -10,8 +14,8 @@ All notable changes to this project will be documented in this file. - selects the first choice (if there is **only one choice** in the dropdown)* - selects the exact match (**case insensitive**; e.g. "apple" + `Tab` will select the available choice "apple", but also "Apple")* - nothing and focuses the next attribute (default) - - * Selected elements will be marked with a blue (Tab) badge -- Pressing `Delete` inside _Single Choice Dropdowns_ will clear the element + - * Selected elements will be marked with a blue (Tab) badge +- Pressing `Delete` inside _Single Choice Dropdowns_ will clear the element - Importer now automatically removes BOM if present - Better readable format for error message on validation - Renamed _fromImport_ to _parseImport_ on the attribute classses. The base class now by default imports the passed string, removing redundancies on the string-based classes. diff --git a/app/Http/Controllers/ReferenceController.php b/app/Http/Controllers/ReferenceController.php index e1532a678..157826456 100644 --- a/app/Http/Controllers/ReferenceController.php +++ b/app/Http/Controllers/ReferenceController.php @@ -40,7 +40,12 @@ public function getByEntity($id) { $groupedReferences = []; foreach($references as $r) { - $key = $r->attribute->thesaurus_url; + if(isset($r->attribute)) { + $key = $r->attribute->thesaurus_url; + } else { + $key = 'on_entity'; + } + if(!isset($groupedReferences[$key])) { $groupedReferences[$key] = []; } @@ -53,7 +58,7 @@ public function getByEntity($id) { // POST - public function addReference(Request $request, $id, $aid) { + public function addReference(Request $request, int $id, ?int $aid = null) { $user = auth()->user(); if(!$user->can('entity_data_create')) { return response()->json([ @@ -69,18 +74,25 @@ public function addReference(Request $request, $id, $aid) { 'error' => __('This entity does not exist') ], 400); } - try { - Attribute::findOrFail($aid); - } catch(ModelNotFoundException $e) { - return response()->json([ - 'error' => __('This attribute does not exist') - ], 400); - } - $props = array_merge([ + $data = [ 'entity_id' => $id, - 'attribute_id' => $aid - ], $request->only(array_keys(Reference::rules))); + ]; + if(isset($aid)) { + try { + Attribute::findOrFail($aid); + $data['attribute_id'] = $aid; + } catch(ModelNotFoundException $e) { + return response()->json([ + 'error' => __('This attribute does not exist') + ], 400); + } + } + + $props = array_merge( + $data, + $request->only(array_keys(Reference::rules)) + ); $reference = Reference::add($props, $user); return response()->json($reference, 201); } diff --git a/app/Reference.php b/app/Reference.php index 08eea1409..95e658d95 100644 --- a/app/Reference.php +++ b/app/Reference.php @@ -26,11 +26,11 @@ class Reference extends Model const rules = [ 'bibliography_id' => 'required|integer|exists:bibliography,id', - 'description' => 'string|nullable' + 'description' => 'required|string' ]; const patchRules = [ - 'description' => 'string|nullable' + 'description' => 'required|string' ]; public function getActivitylogOptions() : LogOptions diff --git a/database/migrations/2024_12_05_125736_enable_reference_for_entities.php b/database/migrations/2024_12_05_125736_enable_reference_for_entities.php new file mode 100644 index 000000000..9601b6136 --- /dev/null +++ b/database/migrations/2024_12_05_125736_enable_reference_for_entities.php @@ -0,0 +1,38 @@ +disableLogging(); + + Schema::table('references', function (Blueprint $table) { + $table->integer('attribute_id')->nullable()->change(); + }); + + activity()->enableLogging(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + activity()->disableLogging(); + + Reference::whereNull('attribute_id')->delete(); + Schema::table('references', function (Blueprint $table) { + $table->integer('attribute_id')->nullable(false)->change(); + }); + + activity()->enableLogging(); + } +}; diff --git a/resources/js/api.js b/resources/js/api.js index 49c6d8eb6..0ed0b8432 100644 --- a/resources/js/api.js +++ b/resources/js/api.js @@ -491,9 +491,15 @@ export async function addEntityTypeAttribute(etid, aid, rank) { } export async function addReference(eid, aid, data) { - return $httpQueue.add( - () => http.post(`/entity/${eid}/reference/${aid}`, data).then(response => response.data) - ); + if(aid) { + return $httpQueue.add( + () => http.post(`/entity/${eid}/reference/${aid}`, data).then(response => response.data) + ); + } else { + return $httpQueue.add( + () => http.post(`/entity/${eid}/reference`, data).then(response => response.data) + ); + } } export async function getFilteredActivity(pageUrl, payload) { diff --git a/resources/js/bootstrap/stores/entity.js b/resources/js/bootstrap/stores/entity.js index e1e1f877d..7da773cf1 100644 --- a/resources/js/bootstrap/stores/entity.js +++ b/resources/js/bootstrap/stores/entity.js @@ -60,7 +60,7 @@ function updateSelectionTypeIdList(selection) { const handleAddEntityType = (context, typeData, attributes = []) => { context.entityTypeAttributes[typeData.id] = attributes.slice(); context.entityTypes[typeData.id] = typeData; -} +}; const handlePostDelete = (context, entityId) => { const currentRoute = router.currentRoute.value; @@ -87,7 +87,7 @@ const handlePostDelete = (context, entityId) => { } } } -} +}; export const useEntityStore = defineStore('entity', { state: _ => ({ @@ -180,7 +180,7 @@ export const useEntityStore = defineStore('entity', { colors = state.entityTypeColors[id]; } return colors; - } + }; }, getEntityTypeName(state) { return id => { @@ -512,8 +512,8 @@ export const useEntityStore = defineStore('entity', { } } - // Remove the data from the entity. - // We need to do this as the 'replace', 'add' 'remove' + // Remove the data from the entity. + // We need to do this as the 'replace', 'add' 'remove' // operations are calculated based on this value. for(const attributeId in removedData) { if(entity.data[attributeId]) { @@ -544,12 +544,17 @@ export const useEntityStore = defineStore('entity', { }); }, handleReference(entityId, attributeUrl, action, data) { + const entity = this.getEntity(entityId); + let references; + if(attributeUrl) { + references = entity?.references[attributeUrl] || []; + } else { + references = entity?.references.on_entity || []; + } if(action == 'add') { - const references = this.getEntity(entityId)?.references[attributeUrl] || []; references.push(data); return data; } else if(action == 'update') { - const references = this.getEntity(entityId)?.references[attributeUrl] || []; const id = data.id; const refData = data.data; const updateData = data.updates; @@ -561,7 +566,6 @@ export const useEntityStore = defineStore('entity', { reference.updated_at = updateData.updated_at; } } else if(action == 'delete') { - const references = this.getEntity(entityId)?.references[attributeUrl] || []; const idx = references.findIndex(ref => ref.id == data.id); if(idx > -1) { references.splice(idx, 1); diff --git a/resources/js/components/MainView.vue b/resources/js/components/MainView.vue index 60fb01ceb..e5005703d 100644 --- a/resources/js/components/MainView.vue +++ b/resources/js/components/MainView.vue @@ -71,39 +71,70 @@ > {{ t('main.entity.references.empty') }}
- -