1. Go to this page and download the library: Download splitstack/laravel-rome library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
splitstack / laravel-rome example snippets
// config/rome.php
return [
// Path where your .sql view files live
'db_views_path' => database_path('views'),
// Connections used for view operations. Must be configured.
// Views are run against each connection in order.
'db_connections' => ['pgsql'], // e.g. ['pgsql'] or ['analytics', 'reporting']
// --- Multi-tenancy (optional) ---
'tenant_model' => null, // e.g. App\Models\Tenant::class
'tenant_status_column' => 'status',
'tenant_active_status' => 'active',
// Directories scanned when make:dbview offers the model picklist.
// App\Models is always
use Splitstack\Rome\Models\ReadOnlyModel;
class OrderSummaryView extends ReadOnlyModel
{
protected $table = 'order_summary_view';
protected $primaryKey = 'id'; // defaults to 'id' if omitted; override if your view's primary key is different
protected static $proxyTo = Order::class;
protected static array $exclude = ['total_price'];
}
use Illuminate\Database\Eloquent\Model;
use Splitstack\Rome\Concerns\HasReadOnlyMode;
class Product extends Model
{
use HasReadOnlyMode;
// Optional: point fromView() at a separate DB view instead of the model's own table.
// PHP 8.2+ forbids re-declaring the trait property with a different value,
// so set this via booted() rather than a property declaration.
protected static function booted(): void
{
static::$readOnlyView = 'products_summary_view';
}
}
// Query the view; results are ReadOnlyProxy instances
$products = Product::fromView()->where('status', 'active')->get();
$product = Product::fromView()->find($id);
$product->name; // ✓
$product->save(); // ✗ throws ReadOnlyModelException
// findOrFail() throws ModelNotFoundException when the record is missing
$product = Product::fromView()->findOrFail($id);
// Bulk writes are blocked at the builder level, before any SQL is sent
Product::fromView()->update(['status' => 'archived']); // ✗ throws ReadOnlyModelException
use Splitstack\Rome\Models\ReadOnlyProxy;
$proxy = new ReadOnlyProxy($anyModel);
$proxy->toArray(); // ✓
$proxy->toJson(); // ✓
$proxy->save(); // ✗ throws ReadOnlyModelException
use Splitstack\Rome\Database\MaterializedViewRefresher;
(new MaterializedViewRefresher('analytics'))->refresh('order_summary_view', concurrent: true);
use Splitstack\Rome\Concerns\RefreshableMaterializedView;
class OrderSummaryView extends ReadOnlyModel
{
use RefreshableMaterializedView;
}
// Queue a refresh
OrderSummaryView::queueRefresh(concurrent: true, queue: 'low');
// Queue a refresh with tenant context (tenant switching is the caller's responsibility)
OrderSummaryView::queueRefresh(tenantId: $tenant->id, connection: 'analytics');
// Queue a delayed refresh
OrderSummaryView::queueRefreshIn(seconds: 30, concurrent: true, queue: 'low');
// Dispatch synchronously (blocks until complete, goes through the job's lock + logging)
OrderSummaryView::refreshNow(concurrent: true);
use Splitstack\Rome\Database\ViewDialect;
$dialect = ViewDialect::fromConnection('analytics');
$dialect->driver(); // 'pgsql' | 'mysql'
$dialect->supportsMaterializedViews(); // true on pgsql, false on mysql
$dialect->dropView('order_summary_view'); // driver-appropriate DROP VIEW
$dialect->dropMaterializedView('...'); // pgsql only, throws on mysql
$dialect->refreshMaterializedView('...', true); // REFRESH ... CONCURRENTLY
$dialect->uniqueIndexSql(); // pg_indexes / information_schema query
$summary = OrderSummaryView::find($id);
$summary->save(); // ❌ PHPStan: Cannot call save() on OrderSummaryView: this is a ReadOnlyModel.
$summary->proxied()->save(); // ❌ PHPStan: Do not call save() on the result of proxied()…
$summary->underlying(false)->save(); // ❌ PHPStan: Do not call save() on the result of underlying(false)…
$summary->underlying(forceFetch: false)->save(); // ❌ same
$summary->underlying(true)->save(); // ✓ DB-fetched — safe
$summary->update(['status' => 'x']); // ✓ correct write path