woenel/prpcmblmts
woenel/prpcmblmts is a Laravel/PHP package for managing prepaid combo limits. It provides building blocks to define, apply, and track combo plan limits, helping developers add prepaid usage controls to their applications with less boilerplate.
Pull it in and publish
composer require woenel/prpcmblmts
php artisan vendor:publish --provider="Woenel\Prpcmblmts\PrpcmblmtsServiceProvider"
Run php artisan vendor:publish without a tag first to see what's offered — migrations, seeders, and possibly a config file are typically published as separate tags, and you usually want to publish all of them before customizing.
Migrate, then seed
php artisan migrate
php artisan db:seed --class=PrpcmblmtsSeeder
The seeded dataset is large (tens of thousands of barangays), so expect this to take minutes, not seconds. Do it in a local/dev environment first, then commit a database dump or run the same commands in each environment.
Where to look first
vendor/woenel/prpcmblmts/database/migrations/ — confirms exact table names, primary key types (PSGC codes are strings, not auto-increments), and the foreign key chain (region → province → municipality/city → barangay).vendor/woenel/prpcmblmts/database/seeders/ — confirms the class name and whether it uses raw insert() batches or Eloquent.vendor/woenel/prpcmblmts/config/ — if present, tells you whether table names are configurable.First use case
A registration or shipping-address form with four cascading dropdowns. Each select fetches the next level by the parent's PSGC code:
Region (e.g., 1300000000)
└─ Province / HUC (e.g., 1339000000)
└─ City/Municipality (e.g., 1339010000)
└─ Barangay (e.g., 133901001)
Bind models in your own service provider
Don't rely on the package to expose Eloquent models — it's a migration + seeder package. Create thin models in App\Models pointed at the published tables:
class Barangay extends Model
{
protected $table = 'barangays';
public $timestamps = false;
public function city()
{
return $this->belongsTo(City::class, 'city_code', 'code');
}
}
This gives you scopes, accessors, and relationships without fighting the package's namespace.
Cascading selects via AJAX
Route::get('/geo/provinces/{region}', function (string $region) {
return Cache::remember("geo.provinces.$region", 86400, fn () =>
Province::where('region_code', $region)
->orderBy('name')
->get(['code', 'name'])
);
});
Cache every level. The data changes only on PSGC revisions (typically yearly), so a 24-hour cache is conservative.
Store codes, not names
Persist barangay_code on your addresses or users table as a char(10) (or varchar(10)), not the barangay name. Names get re-cased, abbreviated, and renamed; codes are the stable identifier. Denormalize the display name if you need to render it without a join, but treat the code as canonical.
Batch import from existing data
If you have legacy address records with free-text province/city strings, match them against this dataset using a normalization step (lowercase, strip City of, Municipality of, punctuation) and store the resolved code. Don't try to join on names directly — matching will silently fail on common variants like Quezon City vs City of Quezon.
Deployment strategy
Because re-seeding on every deploy is slow and risky, do one of:
mysqldump the geo tables, restore in prod.php artisan geo:seed) that only runs when the table is empty.Extension points
Add your own migration on top of the published tables for anything you need — is_active, sort_order, lat, lng, zip_code. The package won't overwrite a migration you've already published, and adding columns doesn't break the FK chain.
The seeder is heavy. ~42,000 barangays plus ~1,600 cities and ~80 provinces. Watch memory_limit and max_execution_time. If it times out, run the published seeder in chunks via DB::table(...)->insert($chunk) — the package's own seeder may already do this, but verify in vendor/....
Re-running the seeder can duplicate rows. Most seeders of this shape use insert() without truncate() or updateOrInsert(). Always run php artisan migrate:fresh --seed locally, and in shared environments truncate the geo tables first or use truncate + seed inside a transaction.
Foreign keys enforce order. You must seed regions → provinces → cities → barangays. If you're writing a custom seeder, respect that chain, or wrap everything in Schema::disableForeignKeyConstraints() / enableForeignKeyConstraints() if the package's FKs are enabled.
PSGC codes are strings, not integers. Some look numeric (1300000000), but leading-zero cases and HUC reassignments exist. Never cast to int — you'll corrupt the hierarchy and break joins.
Names are inconsistent. Expect City of Manila vs Manila, Sto. Niño vs Santo Niño, and mixed casing across levels. If you need clean display text, add an accessor or a generated name_normalized column rather than trusting the raw field in a UI.
PSGC revisions drift. The dataset is a snapshot. Barangays get merged/split, and HUCs get reclassified, on roughly an annual cadence. If addresses need to survive reclassification, store the code and a denormalized region_name/province_name snapshot at the time of entry.
NCR and HUCs break the four-level assumption. Metro Manila cities have no province; independent highly urbanized cities sit directly under a region. Design your FK columns as nullable and allow province_code = null — do not assume the chain is always four deep.
Indexes matter at 42k rows. Add your own indexes on (province_code, name) and (city_code, name) if the package hasn't. Verify with SHOW INDEX FROM barangays after migrating.
Config table prefixes. If the config file exposes table names, changing them after migration is painful. Decide before you migrate; if you must rename, drop the tables and re-run the migration + seeder rather than hand-renaming.
Debugging dropdowns. When a cascading select is empty, log the request parameter and run the corresponding where against the table directly. Almost every "broken dropdown" is a region/province code mismatch caused by an int cast or a stale cache entry. Clear with php artisan cache:clear after any reseed.
Extension via a custom command. Wrap reseeding in php artisan geo:refresh that (1) truncates, (2) disables FKs, (3) reseeds, (4) re-enables FKs, (5) flushes the geo cache keys. This avoids repeating the same fragile sequence in three places.
How can I help you explore Laravel packages today?