> */ public static function fields(): array { return [ 'label' => ['required', 'string', 'max:200'], 'declared_holder' => ['nullable', 'string', 'max:500'], 'document_reference' => ['nullable', 'string', 'max:1000'], 'source_reference' => ['required', 'string', 'min:10', 'max:2000'], 'source_date' => ['nullable', 'date_format:Y-m-d'], 'resource_status' => ['nullable', 'in:available,contracted,planned,unconfirmed'], 'competent_entity' => ['nullable', 'string', 'max:500'], 'purpose' => ['nullable', 'string', 'max:2000'], 'location_description' => ['nullable', 'string', 'max:2000'], 'boundaries_description' => ['nullable', 'string', 'max:2000'], 'occupancy' => ['nullable', 'string', 'max:2000'], 'condition' => ['nullable', 'string', 'max:2000'], 'encumbrances' => ['nullable', 'string', 'max:4000'], 'restrictions' => ['nullable', 'string', 'max:4000'], 'required_authorizations' => ['nullable', 'string', 'max:4000'], 'transfer_condition' => ['nullable', 'string', 'max:4000'], 'contract_type' => ['nullable', 'in:purchase,sale,lease,operation,management,other'], 'valid_from' => ['nullable', 'date_format:Y-m-d'], 'valid_until' => ['nullable', 'date_format:Y-m-d'], 'area' => ['nullable', 'string', 'regex:/^\d{1,12}(\.\d{1,4})?$/', 'numeric', 'gt:0'], 'area_unit' => ['nullable', 'required_with:area', 'in:m2,ha,km2'], 'amount' => ['nullable', 'string', 'regex:/^\d{1,15}(\.\d{1,2})?$/', 'numeric', 'min:0'], 'currency' => ['nullable', 'required_with:amount', 'regex:/^[A-Z]{3}$/'], 'value_kind' => ['nullable', 'required_with:amount', 'in:asking,indicative,appraised,proposed,negotiated,confirmed'], 'participation_percent' => ['nullable', 'numeric', 'between:0,100'], ]; } /** @param array $input */ public function save(User $actor, string $businessId, array $input, ?string $objectId = null): ExistingBusinessObject { $rules = self::fields(); if (! empty($input['payload']['valid_from'])) { $rules['valid_until'][] = 'after_or_equal:valid_from'; } $data = Validator::make($input, ['version_no' => ['required', 'integer', 'min:1'], 'kind' => ['required', 'in:'.implode(',', self::KINDS)], 'reason' => ['required', 'string', 'min:10', 'max:1000'], 'payload' => ['required', 'array:'.implode(',', array_keys($rules))], ])->validate(); // Cross-field rules operate on the payload itself, rather than sibling root fields. Validator::make($data['payload'], $rules)->validate(); return DB::transaction(function () use ($actor, $businessId, $objectId, $data) { $business = app(BusinessRepository::class)->find($businessId, true); Gate::forUser($actor)->authorize('update', $business); abort_unless($business->version_no === $data['version_no'], 409, 'Versão desactualizada.'); abort_if(! $objectId && ExistingBusinessObject::where('business_id', $businessId)->count() >= 100, 422, 'Máximo de 100 elementos por negócio.'); $object = $objectId ? ExistingBusinessObject::where('business_id', $businessId)->whereKey($objectId)->firstOrFail() : new ExistingBusinessObject; if ($objectId) { abort_unless($object->kind === $data['kind'], 422, 'Crie outro elemento para uma natureza diferente.'); } $object->fill(['business_id' => $businessId, 'kind' => $data['kind'], 'classification' => 'restricted', 'verification_status' => 'pending', 'payload' => $data['payload']]); $object->save(); $business->version_no++; $business->save(); app(ExistingBusinessDrafts::class)->snapshot($business, $actor, $objectId ? 'object_updated' : 'object_added', $data['reason']); return $object; }); } }