MapsFromRecord - viames/pair GitHub Wiki

Pair framework: MapsFromRecord

Pair\Data\MapsFromRecord is the companion contract for read models that can be built from a Pair ActiveRecord.

RecordMapper requires both:

Contract

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

/**
 * Minimal read model that can be mapped from a persistence record.
 */
final readonly class UserBadgeReadModel implements ReadModel, MapsFromRecord {

	/**
	 * Build the badge state.
	 */
	public function __construct(
		public int $id,
		public string $label
	) {}

	/**
	 * Build the badge state from a user record.
	 */
	public static function fromRecord(ActiveRecord $record): static {

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

	}

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

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

	}

	/**
	 * Reuse the public array representation for JSON encoding.
	 *
	 * @return	array<string, mixed>
	 */
	public function jsonSerialize(): array {

		return $this->toArray();

	}

}

With the reusable JSON trait

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

/**
 * Compact API representation for a project.
 */
final readonly class ProjectReadModel implements ReadModel, MapsFromRecord {

	use ArraySerializableData;

	/**
	 * Build the project read model.
	 */
	public function __construct(
		public int $id,
		public string $title
	) {}

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

		return new self((int)$record->id, (string)$record->title);

	}

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

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

	}

}

Notes

  • fromRecord() receives Pair\Orm\ActiveRecord, so application code can accept specific subclasses internally after checking or casting.
  • Keep relation loading deliberate; avoid hidden database work in large collection mappings unless a preloader has already prepared the relation data.
  • For CRUD includes, pair includeReadModels with CrudIncludePreloader when relations would otherwise cause repeated queries.

See also: ReadModel, RecordMapper, CrudController, CrudIncludePreloader.