PHP code example of pushinbr / pam-native-nitro

1. Go to this page and download the library: Download pushinbr/pam-native-nitro 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/ */

    

pushinbr / pam-native-nitro example snippets


use Pam\Nitro\Attributes\Field;
use Pam\Nitro\Attributes\Children;
use Pam\Nitro\Attributes\PrimaryKey;
use Pam\Nitro\Model;
use Pam\Nitro\Relations\ChildrenRelation;

enum MessageType: int
{
    case Text = 1;
    case Image = 2;
}

final class Message extends Model
{
    #[PrimaryKey]
    #[Field]
    public string $id;

    #[Field(indexed: true)]
    public string $chatId;

    #[Field]
    public string $body;

    #[Field]
    public MessageType $type;

    #[Field(indexed: true)]
    public int $createdAt;

    public static function table(): string
    {
        return 'messages';
    }
}

final class Chat extends Model
{
    #[PrimaryKey]
    #[Field]
    public string $id;

    #[Children(Message::class, foreignKey: 'chat_id')]
    public ChildrenRelation $messages;

    public static function table(): string
    {
        return 'chats';
    }
}

$chat->messages->get(function (array $messages): void {
    // Only this chat's rows cross the native boundary.
});

use Pam\Nitro\Batch;
use Pam\Nitro\Nitro;

Nitro::boot('zechat.db');
Nitro::prepare([Message::class], function () use ($chatId): void {
    Message::query()
        ->where('chat_id', $chatId)
        ->latest()
        ->limit(20)
        ->get(function (array $messages): void {
            $this->messages = array_reverse($messages);
        });
});

// Many heterogeneous writes: one native call, one transaction.
Nitro::batch(function (Batch $batch) use ($chat, $messages, $draft): void {
    $batch->save($chat)
        ->saveMany($messages)
        ->delete($draft)
        ->execute('UPDATE "chats" SET "unread" = 0 WHERE "id" = ?', [$chat->id]);
}, function (): void {
    // Committed atomically.
});

// Several screens' worth of data: one native call, one result.
Nitro::fetch([
    'chats' => Chat::query()->limit(50),
    'messages' => Message::query()->where('chat_id', $chatId)->latest()->limit(30),
], function (array $results): void {
    [$this->chats, $this->messages] = [$results['chats'], $results['messages']];
});

Nitro::saveMany($messages, function (): void {
    // Thousands of upserts, one bridge call, one prepared statement,
    // one native transaction.
});

Nitro::replaceMany(
    Message::class,
    $freshMessages,
    ['chat_id' => $chatId],
    function (): void {
        // The old chat snapshot and every new upsert commit atomically,
        // even when the snapshot is larger than one bridge payload.
    },
    function (string $error): void {
        // Native failures arrive here; nothing is thrown.
    },
);

// Failures of calls made without a $failure callback.
Nitro::onFailure(fn (string $error) => error_log($error));

$message->delete();

Nitro::deleteWhere(
    Message::class,
    ['chat_id' => $chatId, 'pending' => false],
);

use Pam\Nitro\Sync\MutationOperation;
use Pam\Nitro\Sync\SyncQueue;

enum SyncEntityKind: int
{
    case Message = 1;
}

SyncQueue::enqueue(
    SyncEntityKind::Message,
    $message->id,
    MutationOperation::Upsert,
    $message->attributes(),
    idempotencyKey: $clientMutationId,
);

#[Field]
public string $preview = '';

use Pam\Nitro\{Model, Nitro};

final class NitroStore
{
    public const array MODELS = [ChatRecord::class, MessageRecord::class, ChatDraftRecord::class /* … */];

    private static bool $ready = false;
    private static bool $booting = false;
    /** @var list<Closure(): int> */
    private static array $pending = [];

    public static function ready(Closure $operation): int
    {
        if (self::$ready) {
            return $operation();
        }
        self::$pending[] = $operation;
        if (!self::$booting) {
            self::$booting = true;
            // The offline cache is best effort: failures are logged, the server stays the source of truth.
            Nitro::onFailure(static fn (string $message) => error_log('Nitro failure: '.$message));
            Nitro::boot('zechat-nitro.db');
            Nitro::prepare(self::MODELS, static function (): void {
                self::$ready = true;
                foreach (array_splice(self::$pending, 0) as $operation) {
                    $operation();
                }
            });
        }
        return 0;
    }
}

// Inbox refresh: the user's chat list is replaced atomically (no stale rows, no empty window).
NitroStore::ready(fn () => Nitro::replaceMany(ChatRecord::class, $records, ['user_id' => $userId]));

// Opening a conversation: four side tables in ONE native call.
NitroStore::ready(fn () => Nitro::fetch([
    'draft' => ChatDraftRecord::query()->where('user_id', $userId)->where('chat_id', $chatId)->limit(1),
    'topics' => ChatTopicRecord::query()->where('user_id', $userId)->where('chat_id', $chatId)->orderBy('position')->limit(200),
    'participants' => ChatParticipantRecord::query()->where('user_id', $userId)->where('chat_id', $chatId)->orderBy('position')->limit(1000),
], function (array $results): void {
    $this->draft = $results['draft'][0] ?? null;
    $this->topics = $results['topics'];
    $this->participants = $results['participants'];
}));