Files
Investidor-V2/app/Http/Controllers/PublicBusinessOpportunityController.php

151 lines
8.4 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Resources\PublicBusinessOpportunityResource;
use App\Models\ExistingBusiness;
use App\Models\Sector;
use App\Services\BusinessProximitySearch;
use App\Services\CountryCatalogue;
use App\Services\ExistingBusinessClassification;
use App\Services\ExistingBusinessDrafts;
use App\Services\InvestmentGeography;
use App\Services\OpportunityClassification;
use App\Services\PublicBusinessLocations;
use App\Services\PublicSearchMetrics;
use App\Services\SectorOrganization;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;
class PublicBusinessOpportunityController extends Controller
{
public function index(Request $request): Response|JsonResponse
{
$filters = $this->filters($request);
$query = $this->publishedQuery();
app(ExistingBusinessClassification::class)->constrain($query, $filters);
$path = app(InvestmentGeography::class)->selection($filters);
if ($path !== []) {
$territoryIds = app(InvestmentGeography::class)->descendants($path[array_key_last($path)]->id);
$query->whereExists(app(PublicBusinessLocations::class)->matchingArea($territoryIds)->selectRaw('1')->whereColumn('l.existing_business_id', 'existing_businesses.id'));
}
if (! empty($filters['q'])) {
$term = trim($filters['q']);
$query->where(fn (Builder $builder) => $builder->whereLike('existing_businesses.public_title', '%'.$term.'%')
->orWhereLike('existing_businesses.activity_code', '%'.$term.'%')
->orWhereLike('existing_businesses.objective', '%'.$term.'%'));
}
if (! empty($filters['sector'])) {
$sector = Sector::whereKey($filters['sector'])->first();
$query->whereIn('existing_businesses.sector_id', $sector ? Sector::whereIn('slug', SectorOrganization::slugs($sector->slug))->pluck('id') : []);
}
if (! empty($filters['modality'])) {
$query->where('existing_businesses.modality', $filters['modality']);
}
$coverage = null;
if (! empty($filters['near_business'])) {
$proximity = app(BusinessProximitySearch::class);
$coverage = $proximity->constrain($query, $proximity->origin($filters['near_business']), (float) $filters['radius_km']);
} else {
$query->orderByDesc('existing_businesses.published_at')->orderBy('existing_businesses.id');
}
$page = $query->paginate($filters['per_page'] ?? 12)->appends($filters);
app(PublicSearchMetrics::class)->candidate($request, 'businesses', $page->total(), $filters);
$results = PublicBusinessOpportunityResource::collection($page)->additional(['geographicCoverage' => $coverage]);
if ($request->expectsJson()) {
return $results->response();
}
return Inertia::render('investment/business-marketplace', ['results' => $results, 'filters' => $filters, 'territorySelection' => $path, 'geographicCoverage' => $coverage,
'classificationOptions' => app(OpportunityClassification::class)->options(),
'authorities' => DB::table('authorities')->where('status', 'published')->orderBy('label_pt_mz')->get(['id', 'label_pt_mz'])]);
}
/**
* @return array<array-key, mixed>
*/
private function filters(Request $request): array
{
return $request->validate([
'q' => ['nullable', 'string', 'max:120'],
'modality' => ['nullable', Rule::in(ExistingBusinessDrafts::MODALITIES)],
'sector' => ['nullable', 'integer', 'min:1'],
'per_page' => ['nullable', 'integer', 'min:1', 'max:50'],
'near_business' => ['nullable', 'required_with:radius_km', 'uuid'], 'radius_km' => ['nullable', 'required_with:near_business', 'numeric', 'min:0.1', 'max:2000'],
'province_id' => ['nullable', 'uuid'], 'district_id' => ['nullable', 'uuid'],
'admin_post_id' => ['nullable', 'uuid'], 'locality_id' => ['nullable', 'uuid'],
...app(ExistingBusinessClassification::class)->filterRules(),
]);
}
public function show(Request $request, string $slug): Response|JsonResponse
{
$record = $this->publishedQuery()->where('existing_businesses.public_slug', $slug)->firstOrFail();
$resource = new PublicBusinessOpportunityResource($record);
if ($request->expectsJson()) {
return $resource->response();
}
$returnFilters = $this->filters($request);
$similarProvince = $record->getAttribute('similar_province_id');
if ($similarProvince !== null && ! app(InvestmentGeography::class)->publicReferences()->where('geo_locations.id', $similarProvince)->exists()) {
$similarProvince = null;
}
return Inertia::render('investment/business-opportunity', ['record' => $resource->resolve($request),
'similarRadiusAvailable' => app(BusinessProximitySearch::class)->available($record->id),
'similarCatalogueUrl' => '/negocios?'.http_build_query(array_filter([
'modality' => $record->modality,
'sector' => $record->sector_id,
'province_id' => $similarProvince,
], fn ($value) => $value !== null && $value !== '')),
'returnUrl' => $returnFilters === [] ? '/negocios' : '/negocios?'.http_build_query($returnFilters),
'interestUrl' => '/negocios/'.rawurlencode($slug).'/manifestar-interesse'.($returnFilters === [] ? '' : '?'.http_build_query($returnFilters))]);
}
public function interest(Request $request, string $slug): Response
{
$record = $this->publishedQuery()->where('existing_businesses.public_slug', $slug)->firstOrFail();
$returnFilters = $this->filters($request);
return Inertia::render('investment/business-interest', [
'record' => (new PublicBusinessOpportunityResource($record))->resolve($request),
'modalities' => ExistingBusinessDrafts::MODALITIES,
'countries' => app(CountryCatalogue::class)->all(),
'returnUrl' => $returnFilters === [] ? '/negocios' : '/negocios?'.http_build_query($returnFilters),
]);
}
/** @return Builder<ExistingBusiness> */
private function publishedQuery(): Builder
{
$publicLocations = app(PublicBusinessLocations::class)->primary()
->leftJoin('geo_locations as province', 'province.id', '=', 'l.province_id')
->leftJoin('geo_locations as district', 'district.id', '=', 'l.district_id')
->leftJoin('geo_locations as locality', 'locality.id', '=', 'l.locality_id')
->whereNull('province.deleted_at')->whereNull('district.deleted_at')->whereNull('locality.deleted_at')
->select('l.existing_business_id', 'l.updated_at as location_recorded_at', 'province.name as province', 'province.id as similar_province_id')
->selectRaw(app(PublicBusinessLocations::class)->sourceProjection())
->selectRaw("CASE WHEN l.precision_level IN ('district','admin_post','locality','exact') THEN district.name ELSE NULL END AS district")
->selectRaw('NULL::text AS locality');
return ExistingBusiness::query()->published()->with('sector:id,name,slug')
->leftJoinSub($publicLocations, 'public_location', 'public_location.existing_business_id', '=', 'existing_businesses.id')
->select(['existing_businesses.id', 'existing_businesses.public_slug', 'existing_businesses.public_title', 'existing_businesses.modality',
'existing_businesses.activity_code', 'existing_businesses.stage', 'existing_businesses.objective',
'existing_businesses.currency', 'existing_businesses.amount_min', 'existing_businesses.amount_max',
'existing_businesses.available_percentage', 'existing_businesses.published_at', 'existing_businesses.sector_id',
'existing_businesses.owner_user_id', 'existing_businesses.organization_id', 'existing_businesses.version_no', 'existing_businesses.expires_at', 'existing_businesses.status', 'existing_businesses.modality',
'public_location.province', 'public_location.district', 'public_location.locality', 'public_location.similar_province_id',
'public_location.location_recorded_at', 'public_location.location_source_reference', 'public_location.location_source_date']);
}
}