Search by

islam016 / arabic-search

Islam016

Arabic-aware text normalization and search for PHP and Laravel: find "أحمد" when users type "احمد", ignore harakat, tatweel, ة/ه, ى/ي and Arabic-Indic digits, in PHP and in SQL (MySQL, PostgreSQL, SQLite).

Package info

github.com/Islam016/arabic-search

pkg:composer/islam016/arabic-search

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-29 11:33 UTC

This package is auto-updated.

Last update: 2026-10-29 11:51:23 UTC


README

tests PHP Laravel License

Search Arabic text the way users actually type it.

A user searches for احمد, but the database has أحمد, إحمد or أَحْمَد. They type مدرسه, but the record says مدرسة. They paste an invoice number in Arabic-Indic digits, ١٢٣٤, but you stored 1234. A plain LIKE misses all of these, and that is a real problem in almost every Arabic app: HR systems, e-commerce, CRMs, government portals.

This package normalizes Arabic text in PHP and inside SQL, so these spelling variants match:

Rule Example
Alef forms أ إ آ ٱ → ا أحمد · إحمد → احمد
Harakat & dagger alef removed أَحْمَدُ · هٰذا → احمد · هذا
Tatweel removed محـــمد → محمد
ى and Persian ی → ي مصطفى → مصطفي
ة → ه مدرسة → مدرسه
Persian ک → ك کتاب → كتاب
Arabic-Indic & Persian digits → 0-9 ١٢٣٤ · ۲۰۲۶ → 1234 · 2026
Latin lowercased, whitespace collapsed Laravel مطور → laravel مطور
(optional) ؤ ئ → ء مسؤول · مسئول → مسءول

Works with MySQL / MariaDB, PostgreSQL and SQLite. Every rule is tested against all three on each push.

Installation

composer require islam016/arabic-search

Laravel discovers the service provider automatically. To change the rules, publish the config:

php artisan vendor:publish --tag=arabic-search-config

Plain PHP (any framework)

use IslamGaber\ArabicSearch\ArabicNormalizer;

$arabic = ArabicNormalizer::make();

$arabic->normalize('مؤسسةُ أحمد للتجارة');   // "مؤسسه احمد للتجاره"
$arabic->equals('أحمد إبراهيم', 'احمد ابراهيم'); // true
$arabic->contains('مدرسة الأمل الأهلية', 'الامل'); // true
$arabic->words('شركة واجز، للمقاولات - ٢٠٢٦');   // ["شركه", "واجز", "للمقاولات", "2026"]

Turn rules on or off:

use IslamGaber\ArabicSearch\NormalizerOptions;

$arabic = ArabicNormalizer::make(new NormalizerOptions(
    unifyTaaMarbuta: false,
    unifyHamzaCarriers: true,
));

Laravel

1. Quick search with no schema changes

Query builder macros normalize both the column (in SQL) and the search term (in PHP):

// Finds أحمد, إحمد, أَحْمَد علي ...
User::whereArabicLike('name', $request->q)->get();

// Search several columns, grouped in parentheses so it never breaks your other conditions
Product::where('active', true)
    ->whereArabicSearch(['name', 'description'], $request->q)
    ->paginate();

// Match modes: contains (default), starts_with, ends_with, exact
use IslamGaber\ArabicSearch\LikePattern;

City::whereArabicLike('name', 'جده', LikePattern::EXACT)->first(); // finds "جدة"

DB::table('customers')->orWhereArabicLike('company', 'الامل');

% and _ typed by the user are treated as literal characters, not wildcards.

2. Fast search on large tables (indexed)

On big tables, normalizing the column for every row is slow. Store a normalized copy instead, and the database can use an index.

Migration:

Schema::table('customers', function (Blueprint $table) {
    $table->arabicSearchColumn('name');     // adds an indexed, nullable "name_search" column
    $table->arabicSearchColumn('company');  // "company_search"
});

Model:

use IslamGaber\ArabicSearch\Laravel\HasArabicSearch;

class Customer extends Model
{
    use HasArabicSearch;

    // source column => normalized column
    protected array $arabicSearchable = [
        'name' => 'name_search',
        'company' => 'company_search',
    ];
}

The normalized columns are filled automatically whenever a model is saved. Search them with the scope:

Customer::arabicSearch('احمد')->get();                         // all configured columns
Customer::arabicSearch('احمد', ['name'])->get();               // only some columns
Customer::arabicSearch('احمد', null, LikePattern::STARTS_WITH) // can use the index
    ->limit(10)
    ->get();

Columns that are not in $arabicSearchable fall back to whereArabicLike() automatically.

Existing rows: fill the new columns once (it also rebuilds them if you change the rules):

php artisan arabic-search:backfill "App\Models\Customer"

Helpers

Str::arabicNormalize('مدرسةُ الأمل');            // "مدرسه الامل"
app(ArabicNormalizer::class)->normalize($text);  // configured instance

How it works

Database SQL used for the column
MySQL / MariaDB / SQL Server LOWER(REPLACE(REPLACE(...)))
PostgreSQL LOWER(TRANSLATE(column, 'أإآ...', 'ااا...')), a single call
SQLite arabic_normalize(column), a PHP function registered on the connection

You can use the same expression outside Laravel:

$sql = $arabic->sqlExpression('`name`', 'mysql');
$stmt = $pdo->prepare("SELECT * FROM users WHERE {$sql} LIKE ? ESCAPE '!'");
$stmt->execute([LikePattern::build($arabic->normalize($term))]);

In SQL, whitespace collapsing and the rare Quranic annotation marks are not applied (SQLite applies everything, because it runs the PHP code). For exact parity and best performance, use the stored-column approach.

Testing

composer test

The CI runs the suite on PHP 8.2, 8.3 and 8.4 with SQLite, and on real MySQL 8.4 and PostgreSQL 17 servers.

License

MIT © Islam Gaber