agungsugiarto/codeigniter4-eloquent
Brings Laravel's Eloquent ORM to CodeIgniter 4, letting you use expressive Eloquent models, relationships, and query building inside CI4 apps. A lightweight bridge for developers who prefer Eloquent's syntax over raw queries.
agungsugiarto/codeigniter4-eloquent is the bridge that lets you use Laravel's Eloquent ORM (and, since v2.0.1, the Laravel event dispatcher and facades) inside a CodeIgniter 4 application. If you're a Laravel developer dropped into a CI4 codebase — or maintaining a hybrid app where CI4 handles routing and Eloquent handles persistence — this is the package you reach for first.
Minimal setup:
composer require agungsugiarto/codeigniter4-eloquent
app/Config/Services.php (or load it via Config\Modules / Config\Autoload), then run the package's publish command if available to drop a app/Config/Eloquent.php.Config\Database.php connection group — the package reads CI4's database config rather than a Laravel-style config/database.php, so you do not duplicate credentials.Illuminate\Database\Eloquent\Model directly, or the package's base model if you want CI4 helpers mixed in.namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
protected $table = 'invoices';
protected $guarded = ['id'];
protected $casts = ['paid_at' => 'datetime'];
}
First use case: replace a hand-rolled CI4 Model + Query Builder join in one app/Models/ class with an Eloquent model + belongsTo/hasMany. Keep the CI4 model alongside it, swap call sites one at a time, and verify the generated SQL matches via the CI4 query log. Do not attempt a big-bang migration of your entire model layer.
Facade-backed ad-hoc queries. v2.0.1 activates the Laravel facade, so Illuminate\Support\Facades\DB and Illuminate\Support\Facades\Schema are usable in controllers and services:
use Illuminate\Support\Facades\DB;
$totals = DB::table('invoices')
->selectRaw('customer_id, SUM(amount) as total')
->groupBy('customer_id')
->get();
Prefer DB::transaction(fn () => ...) over manual beginTransaction/commit — it handles retries and nested transactions correctly, which is the main reason to reach for the facade at all.
Relationships and eager loading. This is where Eloquent pays for itself over CI4's builder. Always eager-load in list endpoints:
$invoices = Invoice::with(['customer:id,name', 'lines'])
->where('status', 'open')
->orderByDesc('created_at')
->paginate(25);
Model events and observers (new in 2.0.1). With the event dispatcher now wired, creating, saving, deleted, and observer classes fire as you'd expect. Register observers during bootstrap, not inside a controller:
// app/Config/Eloquent.php or your service boot method
Invoice::observe(InvoiceObserver::class);
Use observers for audit trails, slug generation, and cache invalidation — anywhere you'd have reached for CI4 model callbacks ($beforeInsert) before.
Repository + service layering. Keep Eloquent models out of controllers. A thin repository returns Eloquent collections; a service handles business rules. This makes it trivial to swap back to CI4's builder on a specific table if a driver or query doesn't cooperate.
Testing. Because Eloquent is container-agnostic here, you can hydrate models directly in unit tests without booting CI4. For integration tests, keep the CI4 DatabaseTestTrait and assert against the same connection group.
make:model, migrate, or db:seed commands. Schema changes stay in CI4 migrations/Forge; seeders stay in CI4 seeders. Plan your workflow around that before committing.DB and Schema-style facades, not Cache, Queue, Bus, or Event dispatching to real queue drivers. The event dispatcher is synchronous and in-process. Don't design background jobs around it.sqlsrv driver is now registered, so SQL Server connections work through Eloquent without manual driver bootstrapping. You still need the pdo_sqlsrv PHP extension compiled in, and CI4's Config\Database.php connection must declare 'DBDriver' => 'sqlsrv'. Test date and bit/boolean round-tripping early — SQL Server type coercion is the usual source of "why is my cast wrong" bugs.paginate() returns a Laravel LengthAwarePaginator, but the view helpers won't match CI4's pager templates. Either render links manually or wrap the paginator; don't expect $results->links() to look like the rest of the app.Config\Database, not config/database.php. A stray Laravel-style database.php config file will be ignored and lead to confusing "connection not configured" errors.Model::toSql() with bindings, or listen for Illuminate\Database\Events\QueryExecuted on the dispatcher — now that it's wired in, you can drop a logger listener in and get per-query timing for free.CI4 callbacks, migrate them to observers incrementally rather than ripping the old hooks out in one pass.How can I help you explore Laravel packages today?