И так, имеется в голове примерно такое:
1) interface PaymentApiInterface(array credentials) - от него наследуются адаптеры к различным внешним платежным шлюзам.
2) Действующий (пока будет активным только один) адаптер прописываются в контейнер со своими секретными доступами и т. п.
3) class Payment extends \yii\db\ActiveRecord - AR-модель платежа: покупатель, сумма, состояние и пр.
4) abstract class PaymentService - создает Payment, расширяется для создания платежей разных субъектов оплаты разными типами пользователей (пополнение баланса, покупка чего-то, оплата услуг одного типа, другого типа - для всего этого создается свой сервис).
5) abstract class PaymentApiService(PaymentApiInterface, Payment) - умеет выполнять необходимые задачи через PaymentApiInterface, например fetchPaymentUrl() - получать от банка URL для редиректа на оплату. Так же может расширяться для оплат разных субъектов (потому что будут отличаться способы получения данных платежа и плательщика, способы генерации failURL, successURL, backendURL, описания платежа).
6) И вот так это планировалось использовать:
Код: Выделить всё
<?php
class PaymentAction extends \yii\base\Action
{
//пример упрощен
public function run()
{
//сначала отрабатывает PaymentService, создает модель, заполняет, сохраняет
$serviceInstance = new PaymentService(Payment::class);
$serviceInstance->createModel(Yii::$app->getRequest()->getBodyParams());
if ($serviceInstance->save()) {
//на сцену выходит PaymentApiService, он, используя данные модели Payment и текущий PaymentAPI,
//генерирует запрос к банку и приносит нам paymentURL для редиректа
$paymentApi = Yii::$app->get('PaymentAPI');
$payment = $serviceInstance->getModel();
$merchant = new PaymentApiService($paymentApi, $payment);
$paymentUrl = $merchant->fetchPaymentUrl();
$response = Yii::$app->getResponse();
$response->setStatusCode(201);
return ['paymentURL' => $paymentUrl];
} elseif ($serviceInstance->modelIsValid()) {
throw new ServerErrorHttpException(
'Непредвиденная ошибка в ходе сохранения'
);
}
//это REST API, сериалайзер сам обработает ошибки валидации
return $serviceInstance->getModel();
}
}
Например, $payment->user->phone может быть, а может и не быть. А может, PaymentAPI он вообще будет не нужен. Может, будет нужен $payment->user->surname, который вообще есть не у всех типов user. А вдруг $payment->user->homeAddress понадобится, а его вообще нет и не будет?
Короче, полиморфизм ломается на взаимодействии плагина PaymentAPI и модели Payment.
Как можно выйти из такой ситуации (ну, кроме жесткой стандартизации user'ов)?