Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
at main 3.8 kB view raw
1<?php 2 3namespace SocialDept\AtpParity; 4 5use Illuminate\Database\Eloquent\Model; 6use SocialDept\AtpParity\Contracts\RecordMapper as RecordMapperContract; 7use SocialDept\AtpSchema\Data\Data; 8 9/** 10 * Abstract base class for bidirectional Record <-> Model mapping. 11 * 12 * @template TRecord of Data 13 * @template TModel of Model 14 * 15 * @implements RecordMapperContract<TRecord, TModel> 16 */ 17abstract class RecordMapper implements RecordMapperContract 18{ 19 /** 20 * Get the Record class this mapper handles. 21 * 22 * @return class-string<TRecord> 23 */ 24 abstract public function recordClass(): string; 25 26 /** 27 * Get the Model class this mapper handles. 28 * 29 * @return class-string<TModel> 30 */ 31 abstract public function modelClass(): string; 32 33 /** 34 * Map record properties to model attributes. 35 * 36 * @param TRecord $record 37 * @return array<string, mixed> 38 */ 39 abstract protected function recordToAttributes(Data $record): array; 40 41 /** 42 * Map model attributes to record properties. 43 * 44 * @param TModel $model 45 * @return array<string, mixed> 46 */ 47 abstract protected function modelToRecordData(Model $model): array; 48 49 /** 50 * Get the lexicon NSID this mapper handles. 51 */ 52 public function lexicon(): string 53 { 54 $recordClass = $this->recordClass(); 55 56 return $recordClass::getLexicon(); 57 } 58 59 /** 60 * Get the column name for storing the AT Protocol URI. 61 */ 62 protected function uriColumn(): string 63 { 64 return config('parity.columns.uri', 'atp_uri'); 65 } 66 67 /** 68 * Get the column name for storing the AT Protocol CID. 69 */ 70 protected function cidColumn(): string 71 { 72 return config('parity.columns.cid', 'atp_cid'); 73 } 74 75 public function toModel(Data $record, array $meta = []): Model 76 { 77 $modelClass = $this->modelClass(); 78 $attributes = $this->recordToAttributes($record); 79 $attributes = $this->applyMeta($attributes, $meta); 80 81 return new $modelClass($attributes); 82 } 83 84 public function toRecord(Model $model): Data 85 { 86 $recordClass = $this->recordClass(); 87 88 return $recordClass::fromArray($this->modelToRecordData($model)); 89 } 90 91 public function updateModel(Model $model, Data $record, array $meta = []): Model 92 { 93 $attributes = $this->recordToAttributes($record); 94 $attributes = $this->applyMeta($attributes, $meta); 95 $model->fill($attributes); 96 97 return $model; 98 } 99 100 public function findByUri(string $uri): ?Model 101 { 102 $modelClass = $this->modelClass(); 103 104 return $modelClass::where($this->uriColumn(), $uri)->first(); 105 } 106 107 public function upsert(Data $record, array $meta = []): Model 108 { 109 $uri = $meta['uri'] ?? null; 110 111 if ($uri) { 112 $existing = $this->findByUri($uri); 113 114 if ($existing) { 115 $this->updateModel($existing, $record, $meta); 116 $existing->save(); 117 118 return $existing; 119 } 120 } 121 122 $model = $this->toModel($record, $meta); 123 $model->save(); 124 125 return $model; 126 } 127 128 public function deleteByUri(string $uri): bool 129 { 130 $model = $this->findByUri($uri); 131 132 if ($model) { 133 return (bool) $model->delete(); 134 } 135 136 return false; 137 } 138 139 /** 140 * Apply AT Protocol metadata to attributes. 141 */ 142 protected function applyMeta(array $attributes, array $meta): array 143 { 144 if (isset($meta['uri'])) { 145 $attributes[$this->uriColumn()] = $meta['uri']; 146 } 147 148 if (isset($meta['cid'])) { 149 $attributes[$this->cidColumn()] = $meta['cid']; 150 } 151 152 return $attributes; 153 } 154}