Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 Хорошие практики (FAQ) » Применение middlewate или метода authorize в request классе » 29.12.2017 20:02:49

htclog81
Ответов: 0

В каком случае лучше применять middlewate или метод authorize в request классе?

Мой кейс сейчас таков. Пишу скрипт файлового хостинга с Dropbox подобным функционалом.  Нужно в различных случаях проверять права на папки по пути к ним.

К пример http://site.com/files/test/test2/test3/test4 верен ли путь до папки test4? Совпадает ли со структурой в БД? Принадлежит ли все это авторизованному юзеру? Но также нужно проверять другие роуту, по одному из них папка добавляется, по другому перемещается или удаляется. Иногда нужно проверить один путь, а в других случаях скажем перемещения папки проверить нужно два пути, исходный и конечный.

Для всего этого я создал в классе User метод который проходит циклом по пути и делает запросы нужные. На выходе получается найденный объект Folder который нужен в дальнейшем или false если путь не верный или юзеру не принадлежит. Можно конечно в сервис это запихнуть..

Но суть в том где этот метод вызывать в middleware или в request'ах в методе authorize.

Привежу код который работает в middleware и дальнейшее использование в контроллере


    public function handle($request, Closure $next)
    {
		if (!$request->user()->buildCurrentFolderPath($request->route('path_parts'))) {
				
			return redirect()->route('files')->with('status', 'wrong link');
		}
		
		return $next($request);
		
	}

    public function show(Request $request, $path_parts = null)
    {
		
		$user = $request->user();
	
		$folders = $user->current_folder->getChildFolders();
			
        return view('files.files', [
			'folders' => $folders,
			'path_parts' => $path_parts,
			'user' => $user,
		]);
    }

И на всякий модель

	public function buildCurrentFolderPath($path = null) 
	{
		$pathArr = explode('/', $path);
		
		$currentFolderPath = [];
		
	    $currentFolder = $this->rootFolder;
		
		$currentFolderPath[] = $currentFolder;
		
		foreach ($pathArr as $path_part) {
	
			if (!$path_part) continue;
	
			$currentFolder = $this->folders()
				->where("name", $path_part) 
					-> where('parent_id', '=', $currentFolder->id)
						->first();
			
			if (!$currentFolder)  {
				return false;
			}
			
			$currentFolderPath[] = $currentFolder;
			
			$currentFolder->path = $currentFolderPath;
			
		}
		
		$this->current_folder = $currentFolder;
	
	
		return true;
	}

Тут еще в данном методе данные для хлебных крошек собираются, тк при просмотре папки они нужны и что бы два раза в БД не лазить. Но это я постараюсь в другой метод вынести.

Вопрос скорее где правильнее такой метод вызывать?

Насчет использования паттерна nested tree то он нам не подходит решили... Не хотим осложнять вставку и хранить лишнее.

#2 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 22.12.2017 17:14:35

Оплатить именно то есть сделать charge не даст. А вот добавить подписку конечно даст. С точки зрения шлюза юзер имеет право на кучу подписок...

Впрочем мы уже решили их подписками и планами регулярных платежей не пользоваться хоть оно и просто и удобно. А на своей стороне следить за регулярными платежами и делать из крона charge когда подходит время обновить подписку. По большей части нас это не нужно что бы подписка не зависела от одного конкретного шлюза типа braintree.

А у тебя есть опыт подобных разработок?

#3 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 22.12.2017 16:03:54

С БД то и так все понятно... Вот в шлюзе (stripe or braintree)создать случайно две подписки одному юзеру значит они дважды будут снимать регулярные платежи...

Дело в том, что braintree позволяет множество подписок для юзера. При том что каждая снимает регулярные платежи. А вот как мы проектируем наш сайт это один юзер и одна активная подписка одновременно. А если использовать запрос сначала к braintree и затем в случае успеха insert в нашу БД, то реально возникает race condition и может наплодиться подписок в braintree.  И проверка в самом начале процедуры записи в БД не помогает...

Ну пока лид сказал блокировать кнопку и игнорить... И потом еще подумаем

#4 Re: Laravel 5.x » Как в одном сервисе использовать другой? » 19.12.2017 21:51:40

имеет ли данный пользователь редактировать эти данные.

Тут скорее политики нужны https://laravel.com/docs/5.5/authorizat … g-policies

#5 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 17.12.2017 00:46:37

Что много раз жать на кнопку что одновременно нажать на нее в разных браузерах суть то одна повторные запросы..

#6 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 16.12.2017 10:55:09

Сессия не причем. А если из другого браузера или админ добавляет подписку одновременно с юзером. Тут нужен именно мутекс. А вот насчёт добавления карты повторного это похоже на защиту от флуда..

#7 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 16.12.2017 01:15:57

А еще оказалось семафор для блокировки прямо встроен в php http://php.net/manual/ru/function.sem-get.php


Нашел такой пример:


$sem = sem_get(1234, 1);
if (sem_acquire($sem)) {
    //successful lock, go ahead
    sem_release($sem);
} else {
    //Something went wrong...
}

#8 Re: Laravel 5.x » многократн. нажатие "Отправить" пишет в БД. кто как боролся? » 16.12.2017 00:45:35

Да и мне тоже этот  mutex нужно реализовать, но как лучше...

Вижу например вот такое https://github.com/arvenil/ninja-mutex с мемкешем..

Но может и самому написать.. Как я понимаю если скажем User может иметь только одну активную подписку Subscription и нужно избежать race condition во время ее создание.  То блокируем user->id mutex'ом на создание новых подписок, в начале процедуры создания подписки и скажем на минуту. И при успешном создании убираем mutex.

А если например User может иметь несколько кредитных карт Cards и повторно добавляет одну и туже карту... Блокировать должны видимо именно на добавление данной карты, те записать в мемкеш ее уникальный токен из формы и не принимать с данным токеном запросы более

Правильно ли я все понял?

#9 Re: Laravel 5.x » Защита от повторных запросов БД или Redis » 15.12.2017 12:04:59

Я читал это. Но там не до конца конкретно. Да проверять надо на бекенде... Лочить возможность  добавки пока первый запрос не прошел, отсекая повторные. Собственно про то как это лучше сделать я и спрашиваю.

#10 Laravel 5.x » Защита от повторных запросов БД или Redis » 15.12.2017 01:16:41

htclog81
Ответов: 2

Всем привет!

Продолжаю работать над проектом с оплатой и подписками braintree.

Возник вопрос защиты от повторных запросов на подписку пользователя. Собственно если не блокировать кнопку submit их легко воспроизвести. А проверка в начале обработки запроса на то, что пользователь уже подписан, не помогает. Все потому что создание подписки состоит из двух частей запроса через API к шлюзу и добавление в БД. Запрос к Braintree может и пару секунд занять. И возникает race_conditions.

Насчет того что бы сначала создать подписку в БД в статусе "не связана с Braintree", затем обратиться к Braintree и обновить подписку в БД по результатам. Да так можно и тогда данной проблемы нет.. Но так по ряду причин не хочется.

Вопрос как лучше быть? В начале создания подписки залочить юзера на создание новых подписок через поле в БД в users или создать в Cache запись... Понятно дело что в конце запроса когда подписка уже сохранена в БД можно снять лок.  Как то бороться с помощью повторной отправки именно самой формы считаю смысла нет. Ну а если в разных табах для одного и того же юзера подписку добавляем таже ведь проблема..


Код создания подписки

	public function createSubscription($request)
	{	
		
		if (isset($request['payment_method_nonce'])) {
			$gatewayCustomer = $this->findOrCreateCustomer();
			$this -> createPaymentMethod($request['payment_method_nonce'], $gatewayCustomer);
		}
		
		$trial_ends_at = isset($request['trial_ends_at']) ? $request['trial_ends_at'] : null;
		
		$plan = Plan::findOrFail($request["plan_id"]);
		
		
		if (!empty($this->defaultCard)) {
			$gatewaySubscription =  $this -> createGatewaySubscription($plan, $trial_ends_at);
		} 
		
		$subscription = $this->subscriptions()->create([
			'plan_id' => $plan->id,
			'trial_ends_at' => $trial_ends_at,
			'gateway_id' => isset($gatewaySubscription) ? $gatewaySubscription -> id : null,
			'card_id' => isset($this->defaultCard) ? $this->defaultCard->id : null,
		]);
	}

Логика учитывает создание подписки и юзером и админом. И в том числе и с обращением и без к Braintree

#11 Re: Хорошие практики (FAQ) » DRY и выборки » 13.12.2017 23:02:08

Я бы весь request в аргумент метода точно в таком случае выборке по фильтру бы не запихивал..

#12 Re: Laravel 5.x » А можно ли использовать trait в модели на выбор » 08.12.2017 20:31:46

Вообще какую бы Вы выбрали архитектуру для SAAS приложения, которая не привязано жестко к одному шлюзу, а где User может выбирать скажем оплату и подписку картой через Stripe, а оплату и подписку PayPal чеерез Braintree, а может и еще какие то платежные методы и шлюзы для регулярных платежей...

Понятно можно сделать что то похожее на Laravel cashier с наследованием от интерфейса или абстрактного класса и полимформизмом метода, скажем метод newSubscription() для одного метода подписки так реализован, для другого сяк. Также isSubscriebed() OnTrial() итд. Но вот поточнее бы структуру кто порекомендовал.. И как то может с trait'ами для юзера намутить... Что бы вызывали скажем всегда $user->newSubscription() $user->isSubscriebed() но вот где то дали понять метод какого типа наследника использовать...  Может если речь о создании новой именно подписки то что то вроде паттерна фабрика, фабричный метод. А уже для существующей подписки, выбранные метод будет полем модели юзер и в завимости от его значения тот или иной наследник будет подгружаться...

В общем какое то бы простое красивое решение

#13 Re: Laravel 5.x » А можно ли использовать trait в модели на выбор » 08.12.2017 13:17:32

Понял. И от чего тут можно отнаследовать? сделать интерфейс и от него два сервиса? Или от класса User отнаследовать 2 класса, каждый из который подключает свой trait ?

#14 Laravel 5.x » А можно ли использовать trait в модели на выбор » 08.12.2017 02:36:17

htclog81
Ответов: 3

Скажем при одном запросе в контроллере User был с trait BillableStripe а при другом BillableBraintree. Вопрос не столько о синтаксисе и ООП. А скорее о паттернах и практиках. Или тут скорее создать два сервис провайдера включающих зависимость от User и вызывать в контроллере или тот или другой по надобности смотря какую форму отправили?

#15 Re: Хорошие практики (FAQ) » Нужна хорошая практика создания Eloquent объектов. » 03.12.2017 23:37:52

В общем в итоге получилось вот что. В зависимости от условий создания Subscription объекта и связанного объекта в шлюзе, создаю все это в двух разных методах трейта класса User.


public function createSubscriptionByAdmin($request)
{
	
	$plan = Plan::findOrFail($request['plan_id']);
	
	if ($this->hasCard()) {
		if (empty($request['trial_ends_at'])) {
			$response = GatewaySubscription::create([
				'planId' => $plan->gateway_id,
				'paymentMethodToken' => $this->defaultCard->gateway_id,
				'options' => ['startImmediately' => true]
			]);
			
		} else {
			
			$response = GatewaySubscription::create([
				'planId' => $plan->gateway_id,
				'paymentMethodToken' => $this->defaultCard->gateway_id,
				'firstBillingDate' => $request['trial_ends_at'],
			]);
			
		}
		
		if (!$response->success) {
				throw new Exception('Gateway failed to create subscription: '.$response->message);
		}
		
		return $this->subscriptions()->create([
			'gateway_id' => $response->subscription->id,
			'card_id' => $this->defaultCard->id, 
			'current_period_end' => $response->subscription->billingPeriodEndDate,
			'plan_id' => $plan->id,
			'trial_ends_at' => $request['trial_ends_at'],
		]);
	} else {
		return $this->subscriptions()->create([
			'plan_id' => $plan->id,
			'trial_ends_at' => $request['trial_ends_at'],
		]);
	}
	
}


public function createSubscription($request)
{
	if (isset($request['payment_method_nonce'])) {
		if ($this->gateway_id) {
			throw new \LogicException('When user have payment method add subscription only with this method');
		} else {
			$this->createGatewayCustomerWithPaymentMethod($request['payment_method_nonce']);
		}
	} else {
		if (!$this->gateway_id) {
			throw new \LogicException('When user have not payment method add subscription only with add payment data');
		} 
	
	}
	
	$plan = Plan::findOrFail($request['plan_id']);
	

	
	if (empty($request['trial_ends_at'])) {
		$response = GatewaySubscription::create([
			'planId' => $plan->gateway_id,
			'paymentMethodToken' => $this->defaultCard->gateway_id,
			'options' => ['startImmediately' => true]
		]);
		
	} else {
		
		$response = GatewaySubscription::create([
			'planId' => $plan->gateway_id,
			'paymentMethodToken' => $this->defaultCard->gateway_id,
			'firstBillingDate' => $request['trial_ends_at'],
		
		]);
		
	}
	
	if (! $response->success) {
		throw new Exception('Gateway failed to create subscription: '.$response->message);
	}

	return $this->subscriptions()->create([
		'gateway_id'   => $response->subscription->id,
		'card_id' => $this->defaultCard->id, 
		'current_period_end' => $response->subscription->billingPeriodEndDate,
		'plan_id' => $plan->id,
		'trial_ends_at' => $request['trial_ends_at'],
	 ]);
	
}

Вызывается в контроллерах так:

Для админа

$user-> createSubscriptionByAdmin();

В Request при этом plan_id и trial_ends_at выбранные админом в форме.

Для юзера

$user-> createSubscription(array_merge(
	$request->all(), [
		"trial_ends_at" => config('services.subscription.trial_ends_in_days') ? Carbon::today()->addDays(config('services.subscription.trial_ends_in_days')) : null,
	]
));

Юзер выбирает plan_id, но не выбирает trial_ends_at он задается на этот случае в конфиге.

Отличия метода создания Subscription для юзера и админа, в том что юзер может и должен в момент создания подписки добавить карту, если ее еще нет. А админ конечно за юзера это сделать не может и если карты у юзера нет добавляет подписку только в БД, а не в шлюзе где карта нужна для автопродления...

Вот и думаю как это все зарефакторить... Какой можно паттерн применить.. Может какой нибудь фабричный метод... Или в класс подписки часть из этого тащить из трейта юзера.

Суть что бы лучше читалось и меньше услоовий и повтора кода. Конечно можно тупо вынести повторящийся код в приватные методы. Жду предложений...

#16 Re: Хорошие практики (FAQ) » Где лучше прописать дату по умолчанию » 03.12.2017 22:25:30

Сделал это таким образом

Конфиг

	'subscription' => [
		'trial_ends_in_days' => 4,
	]

Контроллер в случае если мы эту дату берем из конфига, а не из формы. Те если платит юзер, а не админ, то ему дату не выбирать.


$user-> createSubscription(array_merge(
	$request->all(), [
		"trial_ends_at" => config('services.subscription.trial_ends_in_days') ? Carbon::today()->addDays(config('services.subscription.trial_ends_in_days')) : null,
	]
));

Модель. Трейт юзера, где мы создаем для юзера подписку. От разных форматов даты удалось избавиться, тк я понял что через API можно и просто timestamp передать, узнал у саппорта.

public function createSubscription($request)
{
	if (isset($request['payment_method_nonce'])) {
		if ($this->gateway_id) {
			throw new \LogicException('When user have payment method add subscription only with this method');
		} else {
			$this->createGatewayCustomerWithPaymentMethod($request['payment_method_nonce']);
		}
	} else {
		if (!$this->gateway_id) {
			throw new \LogicException('When user have not payment method add subscription only with add payment data');
		} 
	
	}
	
	$plan = Plan::findOrFail($request['plan_id']);
	

	
	if (empty($request['trial_ends_at'])) {
		$response = GatewaySubscription::create([
			'planId' => $plan->gateway_id,
			'paymentMethodToken' => $this->defaultCard->gateway_id,
			'options' => ['startImmediately' => true]
		]);
		
	} else {
		
		$response = GatewaySubscription::create([
			'planId' => $plan->gateway_id,
			'paymentMethodToken' => $this->defaultCard->gateway_id,
			'firstBillingDate' => $request['trial_ends_at'],
		
		]);
		
	}
	
	if (! $response->success) {
		throw new Exception('Gateway failed to create subscription: '.$response->message);
	}

	return $this->subscriptions()->create([
		'gateway_id'   => $response->subscription->id,
		'card_id' => $this->defaultCard->id, 
		'current_period_end' => $response->subscription->billingPeriodEndDate,
		'plan_id' => $plan->id,
		'trial_ends_at' => $request['trial_ends_at'],
	 ]);
	
}

Ну а в случае с админом, я просто через js календарик в нужном формате дату запрашиваю и ничего так же преобразовывать не приходиться. Все что нужно в Request

	public function createSubscriptionByAdmin($request)
	{
		
		$plan = Plan::findOrFail($request['plan_id']);
		
		if ($this->hasCard()) {
			if (empty($request['trial_ends_at'])) {
				$response = GatewaySubscription::create([
					'planId' => $plan->gateway_id,
					'paymentMethodToken' => $this->defaultCard->gateway_id,
					'options' => ['startImmediately' => true]
				]);
				
			} else {
				
				$response = GatewaySubscription::create([
					'planId' => $plan->gateway_id,
					'paymentMethodToken' => $this->defaultCard->gateway_id,
					'firstBillingDate' => $request['trial_ends_at'],
				]);
				
			}
			
			if (!$response->success) {
					throw new Exception('Gateway failed to create subscription: '.$response->message);
			}
			
			return $this->subscriptions()->create([
				'gateway_id' => $response->subscription->id,
				'card_id' => $this->defaultCard->id, 
				'current_period_end' => $response->subscription->billingPeriodEndDate,
				'plan_id' => $plan->id,
				'trial_ends_at' => $request['trial_ends_at'],
			]);
		} else {
			return $this->subscriptions()->create([
				'plan_id' => $plan->id,
				'trial_ends_at' => $request['trial_ends_at'],
			]);
		}
		
	}

До сегодняшних изменений все это в двух сервисных классах SubscriptionBuilder и GenericSubscriptionBuilder было которыя у cashier пакета подсмотрел. Кода меньше стало.  Но осталось эти две метода createSubscription() и createSubscriptionByAdmin() зарефакторить на предмет повторного кода и меньше условий. Благодаря тому что с датой разобрался все куда лучше стало.

#17 Re: Хорошие практики (FAQ) » Где лучше прописать дату по умолчанию » 03.12.2017 18:43:48

Нет, посмотрел свой код мутатор мне не подходит, так как мне нужно менять формат даты не в момент создания объекта именно...

Мне нужно следующее:

1) Принять дату из формы или из конфига
2) Заслать через API дату. Причем там есть варианты и более чистый что ли это через TrialDuration и TrialDurationUnit, а не просто DateTime
3) Использовать дату при создании объекта Subscription

Очевидно что мутатор по крайней мере задачу 2 не решает. Можно конечно написать Хелпер который переводит дату из TimeStamp в TrialDuration и TrialDurationUnit. А перед этим то что в форме перевести в Timestamp

#18 Re: Хорошие практики (FAQ) » Где лучше прописать дату по умолчанию » 03.12.2017 12:10:11

Почему модель не должна знать?

Допустим есть метод

class ExampleModel
{
  function create($request) 
  {
      $request['trial_end'] = fromSomeFormatDate($request['trial_end']);
  }

И вот этот fromSomeFormatDate() зависит от того, что мы сделали в форме и интерфейсе? Кажется совсем не красиво. Тк в форме то мы захотим в таком формате у юзера брать дату, а затем заказчик скажет некрасиво давайте в чуть другом, а мы уже модель меняем. А в метод create модели данные могут и не только из данной формы поступать...

Про сервис понятно, но допустим его не приминяем... А как в мутаторе? Можешь пример привести?

#19 Re: Хорошие практики (FAQ) » Где лучше прописать дату по умолчанию » 03.12.2017 11:35:37

Спасибо так и сделаю.

А если я хочу из формы принимать дату. То преобразовать ее из того что прислала форма ну например html 5 поле с датой или js календарь итд в Carbon дату где лучше? Мне кажется модель не должна знать что там за дата в форм, значки в контроллере или в реквесте быть может?

Как красивее? В реквесте по любому дату ведь проверять

#20 Хорошие практики (FAQ) » Где лучше прописать дату по умолчанию » 03.12.2017 01:23:49

htclog81
Ответов: 7

Хочу прописать дату окончания триального периода подписки по умолчанию.

Где это лучше сделать?

В конфиг дату прописать не получается.


'subscription' => [
   'trialEndsAt' => \Carbon::today()->addDays(1),
]

Не работает.

В класс Subscription прописать константу или статическую переменную имеющую значение Carbon::today()->addDays(1) не получается.

Как вообще принято даты по умолчанию прописывать?

Нет конечно можно прописать период по умолчанию через durationUnit и duration, но это две переменные и вообще хочется иметь все таки именно дату по умолчанию.

Как быть???

#21 Re: Хорошие практики (FAQ) » Класс User и его большие traits » 02.12.2017 12:06:51

а проблему твою не понял.ты пишешь$user->subscription->isTrial()а так$subscription->isTrial()

Конечно будет работать и так и сяк. Я скорее о том, что везде будет достаточно $user->subscription->isTrial() в контроллерах.. Так как везде есть юзер...

В общем я понял, что подход верный...

#22 Хорошие практики (FAQ) » Класс User и его большие traits » 01.12.2017 22:36:03

htclog81
Ответов: 2

Возник каверзный вопрос по архитектуре.


В SAAS приложении, почти все можно делать в trait класса User, ну или в самом классе, просто trait повышает читаемость, понятно что кроме User никакой другой класс не подключит.

Если брать через отношения то к User относятся несколько классов - все связанное с оплатой и подпискам - его карты, подписки, платежи,  так некоторые сервисные вещи, к примеру картинки, файлы юзера итд.

Так вот получается, что можно в контроллерах все время дергать, что то типа $user->subscription->isTrial() $user->card->default $user->mainAvatar() примеры и названия совершенно случайны..  В общем управлять через отношения моделями дочерних юзеру моделей.  Ну или запихнуть в сам класс/используемый им трейт, что все эти обращения к дочерним моделям и вызывать типа $user->trialSubscription() $user->defaultCard() $user->mailAvatar().

И все это вместо $subscription->isTrial() $card->default() $avatar->mainJpg(); итд. итп.


Но не противоречит ли все это тому, что класс не должен быть раздут и быть супер-классом?  Как с этим бороться. Правильный ли путь более менее сложные фичи связанные с дочерними моделями юзера сгонять в сервисный слой и дергать его все равно через класс/трейт юзера?

Понимаю, что вопрос слишком общий... Интересует подход в целом. Что бы не зайти в тупик...

#23 Re: Хорошие практики (FAQ) » Что валидировать в реквесте? » 01.12.2017 15:35:34

А я вообще сделал по простому

class AddSubscriptionController extends Controller {

	public function __construct() {
		$this->middleware('adminUserIsNotSubscriebed');
	}
namespace App\Http\Middleware\Admin;

use Closure;
use Illuminate\Support\Facades\Config;
use App\Classes\Models\User;
class UserIsNotSubscriebed
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
		$user = User::find($request->user_id);
        
		if ($user->subscribed()) {
            return redirect()->route('admin.subscription', $request->user_id);
        }

        return $next($request);
    }
}

Если юзер с данными ID имеет подписку то из данной формы посылаем и все.

Но да может с политиками как то и лучше

#24 Re: Хорошие практики (FAQ) » Что валидировать в реквесте? » 01.12.2017 15:33:35

Ведь, это класс, который проверяет какой-то абстрактный запрос, а мы его связываем с текущим роутом.

Насчет authorize в request я понял что это не какой то абстрактный запрос, а самый что не на есть текущий. Именно запрос который по текущуму роуту идет и который принимаем в методе контроллера. Алексей поправь если не прав?

#25 Re: Хорошие практики (FAQ) » Нужна хорошая практика создания Eloquent объектов. » 01.12.2017 14:52:54

ачем тебе сохранять price, если она уже есть в Plan?

Цена может быть со скидкой по коду по сравнению с планом. Пока это не нужно, но в кешире заложено когда придется все равно нужно будет прикрутить.

Пусть я сейчас это уберу.. name на случай когда несколько подписок, у нас пока и в обозримом одна, опять же поле можно удалить

card_id в подписках также нужно и скорее и сейчас надо оставить. Тк у user может быть одна карта по умолчанию, но вот именно подписка может продлятся другой.

Можно все убрать. Но делается и немного на вырост...

Кроме того убирание всего этого особо и не упростит.

Главное то продолжительность trial и сам факт его наличия

От вот этого

              if (!$trialDuration || !$trialDurationUnit) {
			$response = GatewaySubscription::create([
				'planId' => $plan->gateway_id,
				'price' => (string) round($plan->cost, 2),
				'paymentMethodToken' => $this->user->defaultCard->gateway_id,
				'trialPeriod' => false,
			]);
			
		} else {
			
			$response = GatewaySubscription::create([
				'planId' => $plan->gateway_id,
				'price' => (string) round($plan->cost, 2),
				'paymentMethodToken' => $this->user->defaultCard->gateway_id,
				'trialPeriod' => true,
				'trialDurationUnit' => $trialDurationUnit,
				'trialDuration' => $trialDuration,
			]);
			
		}
		

Все равно не уйти. Тк подписку надо создать не только у нас в БД, но и в платежном шлюзе. И там у Api свои особенности то есть  вот это все им надо отослать 'trialPeriod', 'trialDurationUnit', 'trialDuration' ну или отсылать в случае если создаем подписку без триала.  А просто дату окончания триала в явном виде им отослать нельзя точно.

Те мне по любому нужен сначала запрос к шлюзу. А затем создание модели подписки.

В пакете на основе которого, я все это делал примерно такая логика и есть https://github.com/laravel/cashier-brai … uilder.php https://github.com/laravel/cashier-brai … llable.php


Другое дело, можно ли вообще запихнуть это именно в метод подписки, а не сервиса? Вот в чем вопрос, а даже не в формате массива с данными..

Если лучше сервисный слой оставить то я и оставлю, ну да уберу лишние поля конечно..

Подвал раздела