You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TransactionAmountCast.php 2.8KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. namespace App\Casts;
  3. use App\Models\Banking\BankAccount;
  4. use App\Utilities\Currency\CurrencyAccessor;
  5. use App\Utilities\Currency\CurrencyConverter;
  6. use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
  7. use Illuminate\Database\Eloquent\Model;
  8. use UnexpectedValueException;
  9. class TransactionAmountCast implements CastsAttributes
  10. {
  11. /**
  12. * Static cache to persist across instances
  13. */
  14. private static array $currencyCache = [];
  15. /**
  16. * Eagerly load all required bank accounts at once if needed
  17. */
  18. private function loadMissingBankAccounts(array $ids): void
  19. {
  20. $missingIds = array_filter($ids, static fn ($id) => ! isset(self::$currencyCache[$id]) && $id !== null);
  21. if (empty($missingIds)) {
  22. return;
  23. }
  24. /** @var BankAccount[] $accounts */
  25. $accounts = BankAccount::with('account')
  26. ->whereIn('id', $missingIds)
  27. ->get();
  28. foreach ($accounts as $account) {
  29. self::$currencyCache[$account->id] = $account->account->currency_code ?? CurrencyAccessor::getDefaultCurrency();
  30. }
  31. }
  32. public function get(Model $model, string $key, mixed $value, array $attributes): string
  33. {
  34. // Attempt to retrieve the currency code from the related bankAccount->account model
  35. $bankAccountId = $attributes['bank_account_id'] ?? null;
  36. if ($bankAccountId !== null && ! isset(self::$currencyCache[$bankAccountId])) {
  37. $this->loadMissingBankAccounts([$bankAccountId]);
  38. }
  39. $currencyCode = $this->getCurrencyCodeFromBankAccountId($bankAccountId);
  40. if ($value !== null) {
  41. return CurrencyConverter::prepareForMutator($value, $currencyCode);
  42. }
  43. return '';
  44. }
  45. /**
  46. * @throws UnexpectedValueException
  47. */
  48. public function set(Model $model, string $key, mixed $value, array $attributes): int
  49. {
  50. $bankAccountId = $attributes['bank_account_id'] ?? null;
  51. if ($bankAccountId !== null && ! isset(self::$currencyCache[$bankAccountId])) {
  52. $this->loadMissingBankAccounts([$bankAccountId]);
  53. }
  54. $currencyCode = $this->getCurrencyCodeFromBankAccountId($bankAccountId);
  55. if (is_numeric($value)) {
  56. $value = (string) $value;
  57. } elseif (! is_string($value)) {
  58. throw new UnexpectedValueException('Expected string or numeric value for money cast');
  59. }
  60. return CurrencyConverter::prepareForAccessor($value, $currencyCode);
  61. }
  62. /**
  63. * Get currency code from the cache or use default
  64. */
  65. private function getCurrencyCodeFromBankAccountId(?int $bankAccountId): string
  66. {
  67. if ($bankAccountId === null) {
  68. return CurrencyAccessor::getDefaultCurrency();
  69. }
  70. return self::$currencyCache[$bankAccountId] ?? CurrencyAccessor::getDefaultCurrency();
  71. }
  72. }