Looking to hire Laravel developers? Try LaraJobs

laravel-jakim-esolat-api maintained by myopensoft

Description
Laravel package for JAKIM e-Solat prayer times API - fetch, cache, and manage Malaysian prayer times
Last update
2026/03/18 10:11 (dev-main)
License
Downloads
9

Comments
comments powered by Disqus

Laravel JAKIM e-Solat API

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads License: MIT

A Laravel package that wraps the JAKIM (Jabatan Kemajuan Islam Malaysia) e-Solat API to fetch, cache, and manage Malaysian prayer times. It provides an intelligent fallback chain (Cache, Database, Live API), an expressive enum-based zone system covering all 58 Malaysian zones, and Artisan commands for automated data management.

Table of Contents

Requirements

  • PHP 8.3 or higher
  • Laravel 11 or 12

Installation

Install the package via Composer:

composer require myopensoft/laravel-jakim-esolat-api

Publish the configuration file:

php artisan vendor:publish --tag="jakim-esolat-api-config"

Publish and run the migration:

php artisan vendor:publish --tag="jakim-esolat-api-migrations"
php artisan migrate

Usage

Using the Facade

The EsolatApi facade provides quick access to prayer time data. It accepts either a SolatZone enum or a plain zone code string.

use Myopensoft\LaravelJakimEsolatApi\Facades\EsolatApi;
use Myopensoft\LaravelJakimEsolatApi\Enums\SolatZone;

// Get today's prayer times for Kuala Lumpur
$prayerTime = EsolatApi::getPrayerTime(SolatZone::WLY01);

// Get prayer times for a specific date
$prayerTime = EsolatApi::getPrayerTime('WLY01', '2026-03-17');

if ($prayerTime) {
    echo $prayerTime->fajr;     // "5:57"
    echo $prayerTime->dhuhr;    // "1:14"
    echo $prayerTime->maghrib;  // "7:18"
    echo $prayerTime->hijri;    // "18 Ramadan 1447"
}

Using Dependency Injection

You can inject the EsolatApi service directly via the contract interface or the concrete class:

use Myopensoft\LaravelJakimEsolatApi\Contracts\EsolatApi;
use Myopensoft\LaravelJakimEsolatApi\Exceptions\EsolatApiException;

class PrayerTimeController extends Controller
{
    public function __construct(
        private readonly EsolatApi $esolatApi,
    ) {}

    public function show(string $zone)
    {
        try {
            $prayerTime = $this->esolatApi->getPrayerTime($zone);
        } catch (EsolatApiException $e) {
            abort(422, $e->getMessage());
        }

        if (! $prayerTime) {
            abort(404, 'Prayer times not available for this zone.');
        }

        return response()->json($prayerTime->toArray());
    }
}

Note: Passing an invalid zone code string throws EsolatApiException. Use SolatZone enum for type-safe zone references.

Using SolatZone Enum

The SolatZone enum provides all 58 Malaysian prayer time zones as type-safe values with rich metadata.

use Myopensoft\LaravelJakimEsolatApi\Enums\SolatZone;

// Use a specific zone
$zone = SolatZone::WLY01;
echo $zone->value;     // "WLY01"
echo $zone->state();   // "Wilayah Persekutuan"
echo $zone->district();// "Kuala Lumpur, Putrajaya"
echo $zone->label();   // "Wilayah Persekutuan | Kuala Lumpur, Putrajaya"

// Get all zones as a key-value array (useful for dropdowns)
$allZones = SolatZone::toArray();
// ["JHR01" => "Johor | Pulau Aur dan Pulau Pemanggil", ...]

// Group zones by state
$grouped = SolatZone::groupByState();
// ["Johor" => [SolatZone::JHR01, SolatZone::JHR02, ...], ...]

// Create from string value
$zone = SolatZone::from('SGR01');    // throws ValueError if invalid
$zone = SolatZone::tryFrom('SGR01'); // returns null if invalid

Accessing Zone Info via Attributes

Each zone case is annotated with a #[ZoneInfo] PHP attribute that stores the state and district metadata.

use Myopensoft\LaravelJakimEsolatApi\Enums\SolatZone;
use Myopensoft\LaravelJakimEsolatApi\Attributes\ZoneInfo;

$zone = SolatZone::SGR01;
$info = $zone->info(); // Returns a ZoneInfo instance

echo $info->state;    // "Selangor"
echo $info->district; // "Gombak, Petaling, Sepang, Hulu Langat, Hulu Selangor, Rawang, S.Alam"

Working with PrayerTimeData DTO

The PrayerTimeData is a readonly data transfer object returned by getPrayerTime(). It contains formatted, display-ready values.

use Myopensoft\LaravelJakimEsolatApi\Facades\EsolatApi;

$data = EsolatApi::getPrayerTime('WLY01');

// Access individual properties
$data->zone;      // "WLY01"
$data->zoneLabel; // "Wilayah Persekutuan | Kuala Lumpur, Putrajaya"
$data->date;      // "2026-03-17"
$data->hijri;     // "18 Ramadan 1447"
$data->fajr;      // "5:57"
$data->syuruk;    // "7:08"
$data->dhuhr;     // "1:14"
$data->asr;       // "4:22"
$data->maghrib;   // "7:18"
$data->isha;      // "8:30"

// Convert to array (useful for JSON responses)
$array = $data->toArray();
// [
//     'zone' => 'WLY01',
//     'zone_label' => 'Wilayah Persekutuan | Kuala Lumpur, Putrajaya',
//     'date' => '2026-03-17',
//     'hijri' => '18 Ramadan 1447',
//     'fajr' => '5:57',
//     'syuruk' => '7:08',
//     'dhuhr' => '1:14',
//     'asr' => '4:22',
//     'maghrib' => '7:18',
//     'isha' => '8:30',
// ]

Using PrayerType Enum for Labels

The PrayerType enum maps prayer type identifiers to their Malay names. This is useful for building multilingual UI labels.

use Myopensoft\LaravelJakimEsolatApi\Enums\PrayerType;

echo PrayerType::Fajr->value;    // "fajr"
echo PrayerType::Fajr->label();  // "Subuh"

echo PrayerType::Dhuhr->value;   // "dhuhr"
echo PrayerType::Dhuhr->label(); // "Zohor"

echo PrayerType::Asr->value;     // "asr"
echo PrayerType::Asr->label();   // "Asar"

echo PrayerType::Maghrib->label(); // "Maghrib"
echo PrayerType::Isha->label();    // "Isyak"
echo PrayerType::Syuruk->label();  // "Syuruk"

// Iterate over all prayer types
foreach (PrayerType::cases() as $type) {
    echo "{$type->label()}: {$type->value}";
}

HijriDateConverter Usage

The HijriDateConverter converts Hijri date strings in YYYY-MM-DD format to a human-readable format.

use Myopensoft\LaravelJakimEsolatApi\Services\HijriDateConverter;

// Default format: "{d} {m} {y}"
echo HijriDateConverter::format('1447-09-18');
// "18 Ramadan 1447"

// Custom format
echo HijriDateConverter::format('1447-09-18', '{m} {d}, {y}');
// "Ramadan 18, 1447"

echo HijriDateConverter::format('1447-01-01');
// "01 Muharram 1447"

The converter supports all 12 Hijri months via the HijriMonth enum:

Code Month Name
01 Muharram
02 Safar
03 Rabiulawwal
04 Rabiulakhir
05 Jamadilawwal
06 Jamadilakhir
07 Rejab
08 Syaaban
09 Ramadan
10 Syawal
11 Zulkaedah
12 Zulhijjah

Artisan Commands

esolat:fetch

Fetches prayer times from the JAKIM e-Solat API and stores them in the database. By default, fetches data for all 58 zones. Zones that already have a full 7 days of data are automatically skipped.

# Fetch all zones
php artisan esolat:fetch

# Fetch a specific zone only
php artisan esolat:fetch --zone=WLY01

# Preview what would be fetched without making any changes
php artisan esolat:fetch --dry-run

esolat:prune

Removes prayer time records with dates in the past to keep the database lean.

# Delete all past records
php artisan esolat:prune

# Keep the last 7 days of history
php artisan esolat:prune --days=7

# Preview how many records would be deleted
php artisan esolat:prune --dry-run

Scheduling

For automated data management, add both commands to your Laravel scheduler:

// Laravel 11+ (routes/console.php)
use Illuminate\Support\Facades\Schedule;

Schedule::command('esolat:fetch')->daily();
Schedule::command('esolat:prune')->daily();

Configuration

After publishing the config file, you can modify config/jakim-esolat-api.php:

return [

    /*
    |--------------------------------------------------------------------------
    | JAKIM e-Solat API Base URL
    |--------------------------------------------------------------------------
    |
    | The base URL for the JAKIM e-Solat API endpoint. You should not need
    | to change this unless JAKIM changes their API location.
    |
    */

    'base_url' => env('ESOLAT_API_URL', 'https://www.e-solat.gov.my/index.php'),

    /*
    |--------------------------------------------------------------------------
    | Cache Configuration
    |--------------------------------------------------------------------------
    |
    | Configure how prayer time data is cached. By default, cached data
    | expires at end of day. Set ttl to a specific number of seconds
    | to override this behavior.
    |
    */

    'cache' => [
        'prefix' => env('ESOLAT_CACHE_PREFIX', 'esolat'),
        'ttl' => null, // null = end of day, or seconds (e.g. 3600)
    ],

    /*
    |--------------------------------------------------------------------------
    | HTTP Retry Configuration
    |--------------------------------------------------------------------------
    |
    | Configure retry behavior for API requests to handle transient failures.
    |
    */

    'timeout' => 15, // seconds

    'retry' => [
        'times' => 3,
        'delay' => 200, // milliseconds
    ],

    'fetch' => [
        'throttle' => 500, // milliseconds delay between API calls
    ],

];

Environment Variables

Variable Default Description
ESOLAT_API_URL https://www.e-solat.gov.my/index.php Base URL of the e-Solat API
ESOLAT_CACHE_PREFIX esolat Prefix for cache keys

Configuration Options

Key Type Default Description
base_url string See above JAKIM e-Solat API base URL
cache.prefix string esolat Cache key prefix
cache.ttl int|null null Cache TTL in seconds. null means cache until end of day.
timeout int 15 HTTP request timeout in seconds
retry.times int 3 Number of retry attempts for failed HTTP requests
retry.delay int 200 Delay between retries in milliseconds
fetch.throttle int 500 Delay between zones in esolat:fetch command (milliseconds)

Testing

The package uses Pest for testing.

composer test

To run tests with coverage:

composer test-coverage

To run static analysis:

composer analyse

To format code with Laravel Pint:

composer format

Credits

License

The MIT License (MIT). Please see License File for more information.