Skip to content

ReadonlyModel

Raxos\Database\Orm\ReadonlyModel is a read-only view on a Model. It shares the model's Backbone, so it reads the same data, caches and relations, but it exposes no mutation at all. It is safe to hand to untrusted consumers such as template engines.

Unlike Model, it is not a base class you extend. It is a single final class that wraps any backbone, and you get one by calling readonly() on a model or a collection.

php
final class ReadonlyModel implements
    ArrayAccess,
    DebuggableInterface,
    ModelInterface

Getting one

SourceResult
Model::readonly()A ReadonlyModel over the same backbone, carrying the model's visibility overrides.
ModelArrayList::readonly()A ModelArrayList in which every model is replaced by its read-only view.
Reading a relation on a ReadonlyModelA ReadonlyModel or a ModelArrayList of them.
ReadonlyModel::readonly()The same instance.

Instance methods

MethodDescription
getValue(string $key): mixedReads a column, macro, embedded value or relation by name or alias. Related models come back read-only.
hasValue(string $key): boolReturns true when the key maps to a property of the model.
toArray(): arrayConverts the model to an array, honoring #[Hidden], #[Visible] and any visibility overrides.
jsonSerialize(): arrayReuses toArray() for json_encode().
makeHidden(array|string $keys): staticReturns another read-only view with the given keys hidden from export.
makeVisible(array|string $keys): staticReturns another read-only view with the given keys forced visible.
only(array|string $keys): staticReturns another read-only view that exports only the given keys.
readonly(): selfReturns $this.

Property access and array access map onto getValue() and hasValue(), so $model->name, $model['name'] and isset($model->name) all work.

What throws

AttemptException
$model->name = 'Bas', $model['name'] = 'Bas', setValue()ReadonlyModelException
unset($model->name), unset($model['name']), unsetValue()ReadonlyModelException
Any method call that is not in the table aboveReadonlyModelException
Reading a key that is not a propertyMissingPropertyException

The catch-all method call is deliberate. A template engine falls back to a method lookup when a name is not a property, and without it {{ item.destroy }} would render as an empty string instead of reporting the problem.

Example

php
<?php
declare(strict_types=1);

$user = User::singleOrFail('usr_1');
$readonly = $user->readonly();

$readonly->name;                          // reads
$readonly->role;                          // ReadonlyModel
$readonly->backbone === $user->backbone;  // true

$twig->render('profile.twig', [
    'user' => $readonly
]);

See read-only models for the narrative version, and Model for the writable side.