RecordMapper - viames/pair GitHub Wiki

Pair framework: RecordMapper

Pair\Data\RecordMapper converts persistence records into explicit Pair v4 read models.

It is intentionally strict: the target class must exist, implement ReadModel, and implement MapsFromRecord.

Basic mapping

use Pair\Data\RecordMapper;

$customer = new Customer(7);
$readModel = RecordMapper::map($customer, CustomerReadModel::class);

return new \Pair\Http\JsonResponse($readModel);

JsonResponse will call toArray() because the mapped object implements ReadModel.

Read model used by the mapper

use Pair\Data\ArraySerializableData;
use Pair\Data\MapsFromRecord;
use Pair\Data\ReadModel;
use Pair\Orm\ActiveRecord;

/**
 * Public account payload used by API responses.
 */
final readonly class AccountReadModel implements ReadModel, MapsFromRecord {

	use ArraySerializableData;

	/**
	 * Build the account payload.
	 */
	public function __construct(
		public int $id,
		public string $name,
		public bool $active
	) {}

	/**
	 * Build the account payload from the persistence model.
	 */
	public static function fromRecord(ActiveRecord $record): static {

		return new self(
			(int)$record->id,
			(string)$record->name,
			(bool)$record->active
		);

	}

	/**
	 * Export the public account payload.
	 *
	 * @return	array<string, mixed>
	 */
	public function toArray(): array {

		return [
			'id' => $this->id,
			'name' => $this->name,
			'active' => $this->active,
		];

	}

}

In a collection

use Pair\Data\RecordMapper;

$rows = [];

foreach (Account::query()->where('active', 1)->get() as $account) {
	$rows[] = RecordMapper::map($account, AccountReadModel::class)->toArray();
}

return \Pair\Api\ApiResponse::jsonResponse(['data' => $rows]);

For CRUD resources, CrudController runs this mapping internally when readModel is configured.

Validation behavior

RecordMapper::map() throws InvalidArgumentException when:

  • the read-model class does not exist
  • the class does not implement Pair\Data\ReadModel
  • the class does not implement Pair\Data\MapsFromRecord

This keeps bad API configuration visible during development instead of silently falling back to persistence serialization.

See also: ReadModel, MapsFromRecord, Payload, CrudController, SearchIndexableReadModel.