PHP code example of splitstack / laravel-rome

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'];
}

$summary = OrderSummaryView::find($id);
$summary->update(['status' => 'shipped']); // returns OrderSummaryView

$order = $summary->underlying(); // hits the database; all attributes present

$order = $summary->underlying(forceFetch: false); // no query; $fillable attributes only, faster but riskier

$order = $summary->proxied(); // no query; $fillable attributes hydrated from the view

> class OrderSummaryView extends ReadOnlyModel
> {
>     protected static $proxyTo = Order::class;
>
>     // Stripped when hydrating via proxied() / underlying(false)
>     protected static array $exclude = ['total_price', 'item_count'];
> }
> 

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';
    }
}

$product = Product::find($id);
$proxy = $product->readonly();

$proxy->name;       // ✓ read
$proxy->toArray();  // ✓ serialisation
$proxy->save();     // ✗ throws ReadOnlyModelException

// 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

'priority_views' => ['base_metrics', 'aggregated_totals'],

use Splitstack\Rome\Jobs\RefreshMaterializedView;

// Basic dispatch
RefreshMaterializedView::dispatch(viewName: 'order_summary_view');

// Concurrent refresh (iew::dispatch(
    viewName: 'order_summary_view',
    concurrent: true,
    tenantId: $tenant->id,   // scopes the dedup lock; does not perform tenant switching
    connection: 'analytics', // overrides rome.db_connections
);

// Custom failure callbacks (closures are serialized automatically)
RefreshMaterializedView::dispatch(
    viewName: 'order_summary_view',
    onFailure: [
        fn (\Throwable $e, $job) => \Sentry\captureException($e),
        fn (\Throwable $e, $job) => Notification::send($admin, new ViewRefreshFailed($job->viewName, $e)),
    ],
);

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
bash
php artisan vendor:publish --tag=rome-config
bash
php artisan vendor:publish --tag=rome-phpstan
bash
php artisan make:dbview order_summary
bash
php artisan make:dbview order_summary --model="App\Models\Order"
bash
# all active tenants
php artisan dbview:regen --multi-tenant

# specific tenants
php artisan dbview:regen --tenants=abc123,def456