laravel-opcua · v4.3.x
Docs · Getting started

Quick start

A real Laravel controller reading a PLC tag in three lines. Plus the canonical Tinker session, a console command, and a queued job — the four shapes you'll write 90% of the time.

Four shapes cover almost every use of laravel-opcua in a real Laravel application. This page walks through each.

Prerequisite: package installed (see Installation) and .env pointing at a reachable OPC UA server.

1 — In a controller

php app/Http/Controllers/DashboardController.php
namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use PhpOpcua\LaravelOpcua\Facades\Opcua;
use PhpOpcua\Client\Types\StatusCode;

class DashboardController extends Controller
{
    public function speed(): JsonResponse
    {
        $dataValue = Opcua::read('ns=2;s=PLC/Speed');

        if (! StatusCode::isGood($dataValue->statusCode)) {
            return response()->json([
                'error' => 'Read failed: ' . StatusCode::getName($dataValue->statusCode),
            ], 503);
        }

        return response()->json([
            'speed_rpm' => $dataValue->getValue(),
            'as_of'     => $dataValue->sourceTimestamp?->format('c'),
        ]);
    }
}

Route it like any other:

php routes/web.php
use App\Http\Controllers\DashboardController;

Route::get('/api/speed', [DashboardController::class, 'speed']);

Opcua::read() opens (or reuses) a session, issues a single OPC UA Read request, returns a DataValue. The facade points at the default connection from config/opcua.php.

2 — In a Tinker session

bash terminal
php artisan tinker
php tinker
>>> use PhpOpcua\LaravelOpcua\Facades\Opcua;

>>> Opcua::read('i=2261')->getValue();
=> "open62541 OPC UA Server"

>>> $refs = Opcua::browse('i=85');
>>> count($refs);
=> 4

>>> Opcua::write('ns=2;s=PLC/Setpoint', 42.5);
=> 0    // 0 = Good status

>>> $eps = Opcua::getEndpoints('opc.tcp://plc.local:4840');
>>> collect($eps)->pluck('securityPolicyUri')->unique()->values()->all();
=> ["http://opcfoundation.org/UA/SecurityPolicy#None", "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"]

Tinker is the fastest exploration loop — no controller, no test, no route. The Opcua facade works exactly as in production.

3 — In a console command

bash terminal
php artisan make:command CapturePlcSpeed
php app/Console/Commands/CapturePlcSpeed.php
namespace App\Console\Commands;

use App\Models\PlcReading;
use Illuminate\Console\Command;
use PhpOpcua\LaravelOpcua\Facades\Opcua;

class CapturePlcSpeed extends Command
{
    protected $signature = 'plc:capture-speed
        {connection=default : The opcua connection name}';

    protected $description = 'Capture the current PLC speed reading';

    public function handle(): int
    {
        $value = Opcua::connection($this->argument('connection'))
            ->read('ns=2;s=PLC/Speed')
            ->getValue();

        PlcReading::create([
            'tag'   => 'PLC/Speed',
            'value' => $value,
        ]);

        $this->info("Captured: {$value}");

        return Command::SUCCESS;
    }
}

Schedule it in routes/console.php:

php routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('plc:capture-speed')->everyMinute();

The kernel's schedule:run (typically wired into cron at 1-minute granularity) fires it. One PLC read per tick — durable, in the database, queryable later.

4 — In a queued job

For more involved work — fetch many tags, transform, persist, notify — wrap it in a Job:

bash terminal
php artisan make:job SamplePlc
php app/Jobs/SamplePlc.php
namespace App\Jobs;

use App\Models\PlcReading;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use PhpOpcua\LaravelOpcua\Facades\Opcua;

class SamplePlc implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public function handle(): void
    {
        $values = Opcua::readMulti([
            ['nodeId' => 'ns=2;s=PLC/Speed'],
            ['nodeId' => 'ns=2;s=PLC/Mode'],
            ['nodeId' => 'ns=2;s=PLC/Health'],
        ]);

        PlcReading::create([
            'speed'  => $values[0]->getValue(),
            'mode'   => $values[1]->getValue(),
            'health' => $values[2]->getValue(),
        ]);
    }
}

Dispatch from anywhere:

php dispatch
\App\Jobs\SamplePlc::dispatch();

The queue worker picks it up. With Horizon, watch it through the dashboard — see Integrations · Horizon and queues.

With the session-manager daemon

For request-driven workloads (HTTP controllers, queue workers that hit OPC UA per job), start the daemon in a separate terminal:

bash terminal — daemon
php artisan opcua:session

The Laravel app autodetects the daemon and routes through it. The OPC UA session is opened once on the daemon side and reused across every request — no per-request handshake.

In production, supervise the daemon; see Session manager · Production supervisor.

What just happened, in one line each

Step Mechanism
composer require ... Auto-discovery wires the service provider + facade
php artisan vendor:publish ... Drops config/opcua.php into the application
Opcua::read(...) Facade → OpcuaManager::__call() → default connection → Client::read()
php artisan opcua:session Boots the SessionManagerDaemon configured from config/opcua.php
Worker calling Opcua::read(...) ManagedClient → IPC → daemon-held session → server

Every layer is documented in detail in the rest of the docs. This page is the first turn.

Where to go next