- **Sentry** wired in bootstrap/app.php via Sentry\Laravel\Integration.
No-op when SENTRY_LARAVEL_DSN is empty.
- **Audit logging** broadened: LogsActivity applied to Household, QrCode,
Trip, Payment with tight logOnly whitelists and named log channels
('household', 'qr_code', 'trip', 'payment') to keep the activity
stream useful.
- **Bulk admin actions**:
POST /admin/households/bulk-approve — skips already-approved /
no-proof / unknown ids, reports per-id outcomes
POST /admin/bulk/users/{suspend,activate} — admins protected
POST /admin/bulk/qr-codes/void — respects state machine transitions
- **FCM push scaffold**: PushDriver contract with LogPushDriver
(default) and FcmPushDriver (activates when FCM_SERVER_KEY is set).
PushChannel adapts notifications. RoutesByPreferences now also
routes via push when push_enabled + fcm_token. toPush() payloads
added to HouseholdApproved / QrBalanceLow / PickupImminent.
- **Reverb WebSocket broadcast**: laravel/reverb installed.
TruckLocationBroadcast fires on every TruckTracker::record().
routes/channels.php authenticates: admins → private-admin.live,
residents → area.{id}.trucks (only if their household barangay is
covered by the service area).
- **PSGC seeder** expanded: all 17 PH regions, 4 NCR districts,
all 17 NCR cities. Sample barangays still carry rectangular
boundaries for point-in-polygon resolution tests.
- **Conditional SPATIAL INDEX migration** for barangays.boundary —
safe no-op until every row has a polygon (i.e., after full PSA
dataset import). Re-running `php artisan migrate` after import
flips the column to NOT NULL and adds the index.
196 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
151 lines
8.9 KiB
PHP
151 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use App\Models\Barangay;
|
|
use App\Models\CityMunicipality;
|
|
use App\Models\Province;
|
|
use App\Models\Region;
|
|
use Illuminate\Database\Seeder;
|
|
use MatanYadaev\EloquentSpatial\Objects\LineString;
|
|
use MatanYadaev\EloquentSpatial\Objects\Point;
|
|
use MatanYadaev\EloquentSpatial\Objects\Polygon;
|
|
|
|
/**
|
|
* PSGC sample for development. Includes:
|
|
* - All 17 official PH regions
|
|
* - The 4 NCR districts (modeled as virtual provinces)
|
|
* - All 17 NCR cities/municipalities
|
|
* - A handful of NCR barangays with rough rectangular boundaries for
|
|
* point-in-polygon resolution tests
|
|
*
|
|
* Replace with the full PSA dataset via `php artisan psgc:import <file>`
|
|
* for production.
|
|
*/
|
|
class SamplePsgcSeeder extends Seeder
|
|
{
|
|
public function run(): void
|
|
{
|
|
$this->seedRegions();
|
|
$this->seedNcrProvincesAndCities();
|
|
$this->seedNcrSampleBarangays();
|
|
}
|
|
|
|
private function seedRegions(): void
|
|
{
|
|
$regions = [
|
|
['psgc_code' => '010000000', 'code' => 'R01', 'name' => 'Ilocos Region', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '020000000', 'code' => 'R02', 'name' => 'Cagayan Valley', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '030000000', 'code' => 'R03', 'name' => 'Central Luzon', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '040000000', 'code' => 'R4A', 'name' => 'CALABARZON', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '170000000', 'code' => 'R4B', 'name' => 'MIMAROPA', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '050000000', 'code' => 'R05', 'name' => 'Bicol Region', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '060000000', 'code' => 'R06', 'name' => 'Western Visayas', 'island_group' => 'Visayas'],
|
|
['psgc_code' => '070000000', 'code' => 'R07', 'name' => 'Central Visayas', 'island_group' => 'Visayas'],
|
|
['psgc_code' => '080000000', 'code' => 'R08', 'name' => 'Eastern Visayas', 'island_group' => 'Visayas'],
|
|
['psgc_code' => '090000000', 'code' => 'R09', 'name' => 'Zamboanga Peninsula', 'island_group' => 'Mindanao'],
|
|
['psgc_code' => '100000000', 'code' => 'R10', 'name' => 'Northern Mindanao', 'island_group' => 'Mindanao'],
|
|
['psgc_code' => '110000000', 'code' => 'R11', 'name' => 'Davao Region', 'island_group' => 'Mindanao'],
|
|
['psgc_code' => '120000000', 'code' => 'R12', 'name' => 'SOCCSKSARGEN', 'island_group' => 'Mindanao'],
|
|
['psgc_code' => '130000000', 'code' => 'NCR', 'name' => 'National Capital Region', 'island_group' => 'Luzon'],
|
|
['psgc_code' => '140000000', 'code' => 'CAR', 'name' => 'Cordillera Administrative Region','island_group' => 'Luzon'],
|
|
['psgc_code' => '160000000', 'code' => 'R13', 'name' => 'Caraga', 'island_group' => 'Mindanao'],
|
|
['psgc_code' => '190000000', 'code' => 'BARMM', 'name' => 'Bangsamoro Autonomous Region in Muslim Mindanao', 'island_group' => 'Mindanao'],
|
|
];
|
|
|
|
foreach ($regions as $r) {
|
|
Region::updateOrCreate(['psgc_code' => $r['psgc_code']], $r);
|
|
}
|
|
}
|
|
|
|
private function seedNcrProvincesAndCities(): void
|
|
{
|
|
$ncr = Region::where('psgc_code', '130000000')->firstOrFail();
|
|
|
|
$districts = [
|
|
['psgc_code' => '133900000', 'code' => 'NCR-1', 'name' => 'NCR, City of Manila, First District'],
|
|
['psgc_code' => '137400000', 'code' => 'NCR-2', 'name' => 'NCR, Second District'],
|
|
['psgc_code' => '137500000', 'code' => 'NCR-3', 'name' => 'NCR, Third District'],
|
|
['psgc_code' => '137600000', 'code' => 'NCR-4', 'name' => 'NCR, Fourth District'],
|
|
];
|
|
foreach ($districts as $d) {
|
|
Province::updateOrCreate(['psgc_code' => $d['psgc_code']], array_merge($d, ['region_id' => $ncr->id]));
|
|
}
|
|
|
|
$manilaDist = Province::where('code', 'NCR-1')->first()->id;
|
|
$secondDist = Province::where('code', 'NCR-2')->first()->id;
|
|
$thirdDist = Province::where('code', 'NCR-3')->first()->id;
|
|
$fourthDist = Province::where('code', 'NCR-4')->first()->id;
|
|
|
|
$cities = [
|
|
['psgc_code' => '133900000', 'code' => 'MNL', 'name' => 'City of Manila', 'province_id' => $manilaDist, 'is_capital' => true],
|
|
['psgc_code' => '137401000', 'code' => 'MND', 'name' => 'City of Mandaluyong','province_id' => $secondDist],
|
|
['psgc_code' => '137402000', 'code' => 'MRK', 'name' => 'City of Marikina', 'province_id' => $secondDist],
|
|
['psgc_code' => '137403000', 'code' => 'PSG', 'name' => 'Pasig City', 'province_id' => $secondDist],
|
|
['psgc_code' => '137404000', 'code' => 'QC', 'name' => 'Quezon City', 'province_id' => $secondDist],
|
|
['psgc_code' => '137405000', 'code' => 'SJN', 'name' => 'San Juan City', 'province_id' => $secondDist],
|
|
['psgc_code' => '137501000', 'code' => 'CAL', 'name' => 'Caloocan City', 'province_id' => $thirdDist],
|
|
['psgc_code' => '137502000', 'code' => 'MAL', 'name' => 'Malabon City', 'province_id' => $thirdDist],
|
|
['psgc_code' => '137503000', 'code' => 'NAV', 'name' => 'Navotas City', 'province_id' => $thirdDist],
|
|
['psgc_code' => '137504000', 'code' => 'VAL', 'name' => 'Valenzuela City', 'province_id' => $thirdDist],
|
|
['psgc_code' => '137601000', 'code' => 'LSP', 'name' => 'Las Piñas City', 'province_id' => $fourthDist],
|
|
['psgc_code' => '137602000', 'code' => 'MKT', 'name' => 'Makati City', 'province_id' => $fourthDist],
|
|
['psgc_code' => '137603000', 'code' => 'MUN', 'name' => 'Muntinlupa City', 'province_id' => $fourthDist],
|
|
['psgc_code' => '137604000', 'code' => 'PRQ', 'name' => 'Parañaque City', 'province_id' => $fourthDist],
|
|
['psgc_code' => '137605000', 'code' => 'PSY', 'name' => 'Pasay City', 'province_id' => $fourthDist],
|
|
['psgc_code' => '137606000', 'code' => 'PAT', 'name' => 'Pateros', 'province_id' => $fourthDist, 'type' => 'municipality'],
|
|
['psgc_code' => '137607000', 'code' => 'TAG', 'name' => 'Taguig City', 'province_id' => $fourthDist],
|
|
];
|
|
foreach ($cities as $c) {
|
|
CityMunicipality::updateOrCreate(['psgc_code' => $c['psgc_code']], array_merge([
|
|
'is_capital' => false,
|
|
'type' => 'city',
|
|
], $c));
|
|
}
|
|
}
|
|
|
|
private function seedNcrSampleBarangays(): void
|
|
{
|
|
$qc = CityMunicipality::where('code', 'QC')->firstOrFail();
|
|
$manila = CityMunicipality::where('code', 'MNL')->firstOrFail();
|
|
$makati = CityMunicipality::where('code', 'MKT')->firstOrFail();
|
|
|
|
$samples = [
|
|
['city' => $qc, 'psgc_code' => '137404036', 'code' => 'QC-DLM', 'name' => 'Diliman', 'lat' => 14.6539, 'lng' => 121.0685],
|
|
['city' => $qc, 'psgc_code' => '137404029', 'code' => 'QC-CMW', 'name' => 'Commonwealth', 'lat' => 14.6970, 'lng' => 121.0780],
|
|
['city' => $qc, 'psgc_code' => '137404014', 'code' => 'QC-BPA', 'name' => 'Bagong Pag-asa', 'lat' => 14.6493, 'lng' => 121.0386],
|
|
['city' => $manila, 'psgc_code' => '133900100', 'code' => 'MNL-ERM', 'name' => 'Ermita', 'lat' => 14.5824, 'lng' => 120.9831],
|
|
['city' => $manila, 'psgc_code' => '133900200', 'code' => 'MNL-MLT', 'name' => 'Malate', 'lat' => 14.5722, 'lng' => 120.9846],
|
|
['city' => $makati, 'psgc_code' => '137602100', 'code' => 'MKT-PBL', 'name' => 'Poblacion', 'lat' => 14.5648, 'lng' => 121.0306],
|
|
['city' => $makati, 'psgc_code' => '137602200', 'code' => 'MKT-BAR', 'name' => 'Bel-Air', 'lat' => 14.5575, 'lng' => 121.0200],
|
|
];
|
|
|
|
foreach ($samples as $b) {
|
|
Barangay::updateOrCreate(
|
|
['psgc_code' => $b['psgc_code']],
|
|
[
|
|
'code' => $b['code'],
|
|
'name' => $b['name'],
|
|
'city_municipality_id' => $b['city']->id,
|
|
'urban_rural' => Barangay::URBAN,
|
|
'boundary' => $this->boxAround($b['lat'], $b['lng'], 0.005),
|
|
'centroid' => new Point($b['lat'], $b['lng'], 4326),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
private function boxAround(float $lat, float $lng, float $half): Polygon
|
|
{
|
|
$points = [
|
|
new Point($lat - $half, $lng - $half, 4326),
|
|
new Point($lat - $half, $lng + $half, 4326),
|
|
new Point($lat + $half, $lng + $half, 4326),
|
|
new Point($lat + $half, $lng - $half, 4326),
|
|
new Point($lat - $half, $lng - $half, 4326),
|
|
];
|
|
|
|
return new Polygon([new LineString($points)], 4326);
|
|
}
|
|
}
|