• Start

Libraries

/

Surqlize (ORM)

Edges and graph

Model SurrealDB graph relations in Surqlize with edge models, traverse them in a SELECT, and create them with RELATE.

SurrealDB relation tables are represented by edge models. An edge model maps to a relation table and declares the models on each end, so traversals and RELATE statements are checked against your types.

An edge model extends Surqlize\Edge\Edge and uses the #[Edge] attribute with the relation table name and the in and out endpoint models.

use Surqlize\Attributes\Edge;
use Surqlize\Attributes\Schema;
use Surqlize\Edge\Edge as EdgeModel;

#[Edge('has_address', in: User::class, out: Address::class)]
#[Schema(HasAddressSchema::class)]
final class HasAddress extends EdgeModel
{
}

Edge models inherit RecordId $in and RecordId $out endpoint properties from Surqlize\Edge\Edge.

Use graph fields inside a model SELECT to traverse relations. Edge::out() and Edge::in() start a traversal in the given direction; chain another out() or in() to reach the far table, optionally with a predicate, then name it with as() and resolve it with fetch().

use App\Models\Address;
use App\Models\HasAddress;
use App\Models\User;
use Surqlize\Edge\Edge;

$query = User::select([
        'name',
        Edge::out(HasAddress::class)
            ->out(Address::class, fn ($address) => $address->postcode->includes('24'))
            ->as('address')
            ->fetch(),
    ])
    ->where(fn ($user) => $user->name->eq('beau'))
    ->fetch('address');

$query->compile();
// SELECT name, ->has_address->address[WHERE postcode INCLUDES '24'] AS address
// FROM user WHERE name = "beau" FETCH address

Edge::out() is a magic static call. If your static analysis setup struggles with it, use the explicit factory.

use Surqlize\Edge\GraphSelectField;
use Surqlize\Query\Ast\GraphDirection;

GraphSelectField::fromEdge(HasAddress::class, GraphDirection::Out)
    ->out(Address::class)
    ->as('address');

An edge instance can query its endpoint tables with in() and out(), which return model queries.

$edge = new HasAddress();

$users = $edge->in()
    ->select(fn ($user) => [$user->name])
    ->where(fn ($user) => $user->age->gt(27))
    ->collectModels();

$addresses = $edge->out()
    ->select(fn ($address) => [$address->postcode])
    ->collectModels();

Model::relate($from) starts a RELATE. Chain edge() with the edge class, with() for the target, and content() for data on the edge.

use Surqlize\Relate\Time;

User::relate($user)
    ->edge(HasAddress::class)
    ->with($address)
    ->content(['primary' => true])
    ->timeout(30, Time::Seconds)
    ->execute();

Both endpoint models must already have RecordId values. The builder validates that the source matches the edge's in endpoint and the target matches the out endpoint. Use set($key, $value) for a single field, and compile() or toSdkQuery() to inspect the statement instead of running it.

Note

Model::relate($from) is model-first only. The source model is the in endpoint and the with() target is the out endpoint.

  • Models for the #[Edge] attribute

  • Schema to define the relation table

  • RELATE for the SurrealQL statement

Was this page helpful?