- GET /partner-stores/nearby — public, residents browse active stores
ranked by distance via ST_Distance_Sphere; PublicPartnerStoreResource
excludes commission rate / owner / permit (only what a buyer needs).
- GET /partner-stores/{uuid} — public details, 404 if not active.
- GET /me/collections — paginated resident QR scan history with
optional from/to date filters.
- GET /me/upcoming-pickups — finds scheduled/in-progress trips whose
route includes the resident's assigned drop-off point. Returns
household_assigned: false when no household yet.
- GET /me/notifications — paginated database notifications inbox
with unread_only filter + unread_count in meta.
- POST /me/notifications/{id}/read, POST .../mark-all-read,
GET .../unread-count.
- config/cors.php — allow CUSTOMER_APP_URL and any EXTRA_CORS_ORIGINS
to call the API with credentials. Same-origin admin web is
unaffected.
202 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
1.8 KiB
PHP
59 lines
1.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Store;
|
|
|
|
use App\Models\PartnerStore;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class PartnerStoreFinder
|
|
{
|
|
/**
|
|
* Active partner stores within radius (km) of the given coords,
|
|
* ordered by distance ascending. Each result has a distance_meters
|
|
* attribute attached.
|
|
*
|
|
* @return Collection<int, PartnerStore>
|
|
*/
|
|
public function nearby(float $latitude, float $longitude, float $radiusKm = 5.0, int $limit = 25): Collection
|
|
{
|
|
$radiusMeters = $radiusKm * 1000;
|
|
|
|
$rows = DB::table('partner_stores')
|
|
->whereNull('deleted_at')
|
|
->where('status', PartnerStore::STATUS_ACTIVE)
|
|
->whereNotNull('coordinates')
|
|
->select('id')
|
|
->selectRaw(
|
|
'ST_Distance_Sphere(coordinates, ST_SRID(POINT(?, ?), 4326)) AS distance_meters',
|
|
[$longitude, $latitude],
|
|
)
|
|
->whereRaw(
|
|
'ST_Distance_Sphere(coordinates, ST_SRID(POINT(?, ?), 4326)) <= ?',
|
|
[$longitude, $latitude, $radiusMeters],
|
|
)
|
|
->orderBy('distance_meters')
|
|
->limit($limit)
|
|
->get();
|
|
|
|
if ($rows->isEmpty()) {
|
|
return collect();
|
|
}
|
|
|
|
$distancesById = $rows->pluck('distance_meters', 'id');
|
|
$stores = PartnerStore::with(['barangay', 'inventory'])
|
|
->whereIn('id', $rows->pluck('id'))
|
|
->get()
|
|
->keyBy('id');
|
|
|
|
return $rows->map(function ($r) use ($stores, $distancesById) {
|
|
$s = $stores->get($r->id);
|
|
if ($s) {
|
|
$s->distance_meters = (float) $distancesById->get($r->id);
|
|
}
|
|
|
|
return $s;
|
|
})->filter()->values();
|
|
}
|
|
}
|