PHPackages                             innonazarene/dbm-salary-grade-parser - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. innonazarene/dbm-salary-grade-parser

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

innonazarene/dbm-salary-grade-parser
====================================

Salary Grade schedule parser. Look up monthly salaries by grade and step.

v1.0.2(2mo ago)1411MITPHP ^8.1

Since Apr 24Compare

[ Source](https://github.com/innonazarene/dbm-salary-grade-parser)[ Packagist](https://packagist.org/packages/innonazarene/dbm-salary-grade-parser)[ Docs](https://github.com/innonazarene/dbm-salary-grade-parser)[ RSS](/packages/innonazarene-dbm-salary-grade-parser/feed)WikiDiscussions Synced 3w ago

READMEChangelogDependencies (2)Versions (5)Used By (0)

dbm-salary-grade-parser
=======================

[](#dbm-salary-grade-parser)

[![Tests](https://github.com/innonazarene/dbm-salary-grade-parser/actions/workflows/ci.yml/badge.svg)](https://github.com/innonazarene/dbm-salary-grade-parser/actions)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

Philippine Government Salary Grade schedule as a PHP library.

Data source: **National Budget Circular No. 601 (2026)** — Department of Budget and Management (DBM).

---

Requirements
------------

[](#requirements)

- PHP 8.1+
- For PDF extraction: `smalot/pdfparser`

```
composer require smalot/pdfparser
```

Installation
------------

[](#installation)

```
composer require innonazarene/dbm-salary-grade-parser
```

---

Usage
-----

[](#usage)

### Parse a salary schedule PDF

[](#parse-a-salary-schedule-pdf)

```
use PhSalaryGrade\SalaryGradeParser;

// From a PDF file — extracts text then parses the salary table
$schedule = SalaryGradeParser::parseFile('/path/to/nbc601.pdf');

// Parse a specific table region (e.g. "First Class" in LGU circulars)
// The parser will skip everything until it finds the specified keyword
$firstClassSchedule = SalaryGradeParser::parseFile('/path/to/lbc165.pdf', 'First Class');

// $schedule is keyed by salary grade, each value is an array of step amounts
// [
//   1  => [14061, 14164, 14278, 14393, 14509, 14626, 14743, 14862],
//   2  => [14925, 15035, ...],
//   ...
//   30 => [203200, 206401, ...],
// ]

// Access a specific grade + step
$grade15step3 = $schedule[15][2]; // step index is 0-based → 41006

// Or use the SalaryGrade lookup class with built-in NBC 601 data
use PhSalaryGrade\SalaryGrade;
$salary = SalaryGrade::get(15, 3); // → 41006
```

### Parse from raw text (if you already extracted it)

[](#parse-from-raw-text-if-you-already-extracted-it)

```
use PhSalaryGrade\SalaryGradeParser;

$text = file_get_contents('salary_text.txt');
$schedule = SalaryGradeParser::parse($text);

// Or with a keyword to target a specific table
$schedule = SalaryGradeParser::parse($text, 'Special Cities');
```

### Extract text from PDF only

[](#extract-text-from-pdf-only)

```
use PhSalaryGrade\PdfExtractor;

$text = PdfExtractor::extract('/path/to/nbc601.pdf');
echo $text; // raw text from all pages
```

### How extraction works

[](#how-extraction-works)

1. **`PdfExtractor::extract()`** — reads the PDF using `smalot/pdfparser` and reconstructs the table rows mathematically based on the exact \[X, Y\] coordinates of the text on the page!
2. **`SalaryGradeParser::parse()`** — scans each line, finds rows that start with a grade number (1–30) followed by salary amounts in the ₱10,000–₱999,999 range, validates that steps are ascending, and returns a clean array

### Using the static schedule (no PDF needed)

[](#using-the-static-schedule-no-pdf-needed)

```
use PhSalaryGrade\SalaryGrade;

// Get salary for Grade 15, Step 3
$salary = SalaryGrade::get(15, 3);
// → 41006

// Get salary for Grade 10, Step 1 (default)
$salary = SalaryGrade::get(10);
// → 25586

// Get all 8 steps for a grade
$steps = SalaryGrade::getSteps(24);
// → [1 => 98185, 2 => 99721, 3 => 101283, ..., 8 => 109431]

// Get the full salary schedule (all grades and steps)
$all = SalaryGrade::all();
// → [1 => [1 => 14061, ...], 2 => [...], ..., 30 => [...]]

// Find which grade/step a specific amount belongs to
$match = SalaryGrade::find(40208);
// → ['grade' => 15, 'step' => 1]

// Get all grade/step combinations within a salary range
$results = SalaryGrade::range(40000, 45000);
// → [['grade' => 15, 'step' => 1, 'amount' => 40208], ...]

// Convenience: min and max salaries
SalaryGrade::min(); // → 14061  (Grade 1, Step 1)
SalaryGrade::max(); // → 226319 (Grade 30, Step 8)
```

---

Laravel Integration Example (Step-by-Step Workflow)
---------------------------------------------------

[](#laravel-integration-example-step-by-step-workflow)

This package is designed to easily integrate into Laravel applications. Below is the complete structure and workflow for a system where users can upload a PDF, download a generated CSV for review, and upload the reviewed CSV back to the system to be saved.

### 1. Database Migration

[](#1-database-migration)

Create a table to store the salary tranche data:

```
php artisan make:migration create_salary_tranches_table
```

```
public function up()
{
    Schema::create('salary_tranches', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('circular_no')->nullable();
        $table->string('pdf_path')->nullable();
        $table->string('csv_path')->nullable();
        $table->json('parsed_data'); // Stores the array of salary grades
        $table->timestamps();
    });
}
```

### 2. Eloquent Model

[](#2-eloquent-model)

Ensure your model casts the `parsed_data` column to an array so Laravel automatically handles the JSON serialization:

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SalaryTranche extends Model
{
    protected $fillable = ['title', 'circular_no', 'pdf_path', 'csv_path', 'parsed_data'];

    protected $casts = [
        'parsed_data' => 'array',
    ];
}
```

### 3. API Routes

[](#3-api-routes)

Define the endpoints for uploading PDFs, downloading the generated CSV, and uploading the final reviewed CSV:

```
use App\Http\Controllers\SalaryTrancheController;

// Upload a PDF and generate a CSV
Route::post('/salary-tranches', [SalaryTrancheController::class, 'store']);

// Download the generated CSV for review
Route::get('/salary-tranches/{id}/download-csv', [SalaryTrancheController::class, 'downloadCsv']);

// Upload the reviewed CSV to finalize and save the data
Route::post('/salary-tranches/upload-csv', [SalaryTrancheController::class, 'storeCsv']);
```

### 4. Controller Logic

[](#4-controller-logic)

Create the controller to tie it all together:

```
namespace App\Http\Controllers;

use App\Models\SalaryTranche;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use PhSalaryGrade\SalaryGradeParser;

class SalaryTrancheController extends Controller
{
    /**
     * Step 1: Upload a PDF. The package parses it and generates a CSV.
     */
    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'circular_no' => 'nullable|string|max:255',
            'region' => 'nullable|string|max:255', // e.g. "First Class"
            'pdf' => 'required|file|mimes:pdf|max:10240'
        ]);

        $pdfPath = $request->file('pdf')->store('salary-tranches/pdfs', 'public');
        $fullPdfPath = Storage::disk('public')->path($pdfPath);

        // Parse PDF using the package (targets a specific keyword/region if provided)
        $parsedData = SalaryGradeParser::parseFile($fullPdfPath, $request->region);

        // Generate a CSV for user review
        $csvPath = 'salary-tranches/csvs/salary_tranche_' . time() . '.csv';
        Storage::disk('public')->makeDirectory('salary-tranches/csvs');

        $csvFile = fopen(Storage::disk('public')->path($csvPath), 'w');
        fputcsv($csvFile, ['Salary Grade', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5', 'Step 6', 'Step 7', 'Step 8']);

        foreach ($parsedData as $row) {
            fputcsv($csvFile, [
                $row['salary_grade'] ?? '', $row['step_1'] ?? '', $row['step_2'] ?? '',
                $row['step_3'] ?? '', $row['step_4'] ?? '', $row['step_5'] ?? '',
                $row['step_6'] ?? '', $row['step_7'] ?? '', $row['step_8'] ?? ''
            ]);
        }
        fclose($csvFile);

        $tranche = SalaryTranche::create([
            'title' => $request->title,
            'circular_no' => $request->circular_no,
            'pdf_path' => $pdfPath,
            'csv_path' => $csvPath,
            'parsed_data' => $parsedData,
        ]);

        return response()->json([
            'message' => 'PDF parsed and CSV generated.',
            'download_csv_url' => url('/api/salary-tranches/' . $tranche->id . '/download-csv')
        ]);
    }

    /**
     * Step 2: Download the generated CSV for manual review/correction.
     */
    public function downloadCsv($id)
    {
        $tranche = SalaryTranche::findOrFail($id);
        return Storage::disk('public')->download($tranche->csv_path, 'salary_table.csv');
    }

    /**
     * Step 3: Upload the reviewed CSV. Saves data to DB and deletes the CSV automatically.
     */
    public function storeCsv(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'csv' => 'required|file|mimes:csv,txt|max:10240'
        ]);

        // Get the temporary uploaded file
        $csvFile = $request->file('csv');
        $path = $csvFile->getRealPath();

        $parsedData = [];
        if (($handle = fopen($path, 'r')) !== false) {
            fgetcsv($handle); // Skip header
            while (($row = fgetcsv($handle)) !== false) {
                if (!empty($row[0])) {
                    $parsedData[] = [
                        'salary_grade' => (int) $row[0],
                        'step_1' => isset($row[1]) && $row[1] !== '' ? (int) $row[1] : null,
                        'step_2' => isset($row[2]) && $row[2] !== '' ? (int) $row[2] : null,
                        'step_3' => isset($row[3]) && $row[3] !== '' ? (int) $row[3] : null,
                        'step_4' => isset($row[4]) && $row[4] !== '' ? (int) $row[4] : null,
                        'step_5' => isset($row[5]) && $row[5] !== '' ? (int) $row[5] : null,
                        'step_6' => isset($row[6]) && $row[6] !== '' ? (int) $row[6] : null,
                        'step_7' => isset($row[7]) && $row[7] !== '' ? (int) $row[7] : null,
                        'step_8' => isset($row[8]) && $row[8] !== '' ? (int) $row[8] : null,
                    ];
                }
            }
            fclose($handle);
        }

        // Store to DB. The uploaded CSV file is automatically deleted by Laravel after this request.
        $tranche = SalaryTranche::create([
            'title' => $request->title,
            'circular_no' => $request->circular_no ?? null,
            'parsed_data' => $parsedData,
        ]);

        return response()->json(['message' => 'Salary tranche stored from CSV successfully!']);
    }
}
```

---

Salary Schedule (NBC No. 601 — 2026)
------------------------------------

[](#salary-schedule-nbc-no-601--2026)

SGStep 1Step 2Step 3Step 4Step 5Step 6Step 7Step 8114,06114,16414,27814,39314,50914,62614,74314,862214,92515,03515,14615,25815,37115,48415,59915,714315,85215,97116,08816,20816,32916,44816,57116,693416,83316,95817,08417,20917,33717,46417,59417,724517,86618,00018,13318,26718,40118,53818,67618,813618,95719,09819,23919,38319,52619,67019,81619,963720,11020,25820,40820,56020,71120,86521,01921,175821,44821,64221,83922,03522,23422,43522,63822,843923,22623,41123,59923,78823,97824,17024,36424,5581025,58625,79025,99626,20326,41226,62326,83527,0501130,02430,30830,59730,88931,18531,48631,79032,0991232,24532,52932,81733,10833,40333,70234,00434,3101334,42134,73335,04935,36935,69436,02236,35436,6911437,02437,38437,74938,11838,49138,86939,25239,6401540,20840,60441,00641,41341,82442,24142,66243,0901643,56043,99644,43844,88545,33845,79646,26146,7301747,24747,72748,21348,70549,20349,70850,21850,7351851,30451,83252,36752,90753,45654,01054,57255,1401956,39057,16557,95358,75359,56760,39461,23562,0892062,96763,84264,73265,63766,55767,47968,40969,3422170,01371,00072,00473,02474,06175,11576,15177,2392278,16279,27780,41181,56482,73583,88785,09686,3242387,31588,57489,85591,16392,59294,04395,51896,9552498,18599,721101,283102,871104,483106,123107,739109,43125111,727113,476115,254117,062118,899120,766122,664124,59126126,252128,228130,238132,280134,356136,465138,608140,78827142,663144,897147,169149,407151,752153,850156,267158,72328160,469162,988165,548167,994170,634173,320175,803178,57229180,492183,332186,218189,151192,131194,797197,870200,99330203,200206,401209,558212,766216,022219,434222,797226,319---

Running Tests
-------------

[](#running-tests)

```
composer install
composer test
```

---

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance83

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

88d ago

Major Versions

v0.1 → v1.0.02026-04-27

### Community

Maintainers

![](https://www.gravatar.com/avatar/b6dc3457db242c3406b720fbc74b3e6ba97bb1472cde2fc50d5936ca4e98cfbe?d=identicon)[inonazarene](/maintainers/inonazarene)

---

Tags

salary grade

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/innonazarene-dbm-salary-grade-parser/health.svg)

```
[![Health](https://phpackages.com/badges/innonazarene-dbm-salary-grade-parser/health.svg)](https://phpackages.com/packages/innonazarene-dbm-salary-grade-parser)
```

###  Alternatives

[krayin/laravel-crm

Krayin CRM

23.2k33.6k1](/packages/krayin-laravel-crm)[j0k3r/graby

Graby helps you extract article content from web pages

388369.3k2](/packages/j0k3r-graby)[theodo-group/llphant

LLPhant is a library to help you build Generative AI applications.

1.7k409.0k6](/packages/theodo-group-llphant)[horstoeko/zugferd

A library for creating and reading european electronic invoices

4295.8M31](/packages/horstoeko-zugferd)[atgp/factur-x

PHP library to manage your Factur-X / ZUGFeRD 2.0 PDF invoices files

153915.3k4](/packages/atgp-factur-x)[helgesverre/receipt-scanner

Use OpenAI to extract structured receipt and invoice data from Text, Html, Images and PDFs.

1439.4k](/packages/helgesverre-receipt-scanner)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
