# Mutations

Create, update, upsert, and delete records in Surqlize with model helpers and the mutation builder, including payload modes and return modes.

Surqlize exposes mutations two ways. Model helpers such as `create()` and `save()` cover the common cases and return hydrated models. The mutation builder gives full control over the payload, the return clause, and conditional updates.

## Model helpers

### Create

`create()` inserts a record and returns the model. Pass an `id` to set a specific record id.

```php
$user = User::create(['name' => 'beau', 'age' => 27]);

$user = User::create(['name' => 'beau', 'age' => 27], id: 'beau');
```

### Save

`save()` creates the record when the model has no `RecordId`, and updates it when it does.

```php
$user = User::findOrFail('beau');
$user->age = 28;
$user = $user->save();
```

### Upsert

`upsert()` creates the record if it does not exist, or updates it if it does. It requires an id.

```php
$user = User::upsert(['name' => 'beau', 'age' => 27], id: 'beau');
```

### Delete

`delete()` removes the record the model points at.

```php
$user = User::findOrFail('beau');
$user->delete();
```

### Reading records

The read helpers return models or scalars.

| Method | Purpose |
|--------|---------|
| `all()` | Fetch every record as models |
| `find($id)` | Find one model by id, or `null` |
| `findOrFail($id)` | Find one model by id, or throw `ModelNotFoundException` |
| `count($where?)` | Count records, optionally filtered |
| `exists($where?)` | Whether at least one matching record exists |
| `refresh()` | Reload the model instance from the database |

```php
$count = User::count(fn ($user) => $user->age->gte(18));

if (User::exists(fn ($user) => $user->name->eq('beau'))) {
    // ...
}
```

## The mutation builder

For more control, build the mutation explicitly. `createQuery()` returns a builder instead of running immediately, and `updateWhere()` and `deleteWhere()` target records by predicate.

```php
$query = User::createQuery(['name' => 'beau', 'age' => 27], id: 'beau');
$query->compile();
// CREATE user:beau CONTENT {"name":"beau","age":27} RETURN AFTER
```

Update or delete records that match a condition.

```php
User::updateWhere(fn ($user) => $user->age->gte(18))
    ->merge(['verified' => true])
    ->returnAfter()
    ->execute();

User::deleteWhere(fn ($user) => $user->age->lt(13))
    ->returnBefore()
    ->execute();
```

### Payload modes

| Method | SurrealQL |
|--------|-----------|
| `content($data)` | `CONTENT` |
| `merge($data)` | `MERGE` |
| `replace($data)` | `REPLACE` |
| `patch($patches)` | `PATCH` |

### Return modes

| Method | SurrealQL |
|--------|-----------|
| `returnNone()` | `RETURN NONE` |
| `returnBefore()` | `RETURN BEFORE` |
| `returnAfter()` | `RETURN AFTER` |
| `returnDiff()` | `RETURN DIFF` |
| `returning($fields)` | Return selected fields |
| `returningValue($field)` | Return one selected value |

### Running the mutation

`timeout($amount, $unit = 's')` adds a statement timeout. To run the mutation, call one of:

| Method | Result |
|--------|--------|
| `execute()` | The raw SDK result |
| `executeModels()` | A list of hydrated models |
| `firstModel()` | The first hydrated model, or `null` |
| `compile()` | A literal SurrealQL string |
| `toBoundQuery()` | An SDK [`BoundQuery`](/docs/reference/php/v2/api/utilities.md#boundquery) |

```php
$user = User::createQuery(['name' => 'beau'])
    ->returnAfter()
    ->firstModel();
```

## Learn more

- [Querying](/docs/reference/php/libraries/surqlize/querying.md) for reading records
- [Transactions](/docs/reference/php/libraries/surqlize/transactions.md) for grouping mutations atomically
- [Connections](/docs/reference/php/libraries/surqlize/connections.md) for per-query executor injection
