Laravel по-русски

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

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

#1 15.11.2017 23:14:32

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Допустим есть платежные методы данного юзера и только один из них является методом по умолчанию. Признака is_default у платежного метода нет. Зато у юзера есть поле в котором храниться id метода по умолчанию

class PaymentMethod extends Model
{
....
		
    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }
...
}

class User extends Authenticatable
{
    ...
	
    public function payment_methods()
    {
        return $this->hasMany(AppPaymentMethod::class, $this->getForeignKey())->orderBy('created_at', 'desc');
    }

    public function default_payment_method()
    {
        return $this->hasOne(AppPaymentMethod::class, 'id', 'default_payment_method_id');
    }
    public function getDefautPaymentMethod()
	{
		$paymentMethod = $this->default_payment_method()->first();
		return $paymentMethod;
	}
	

...
}

Как для юзера взять в шаблоне его умолчательный метод ясно и работает

 
  				@if($user -> getDefautPaymentMethod()->type == PaymentMethod::PAYPAL_ACCOUNT)
					<div><strong>Paypal: </strong> {{$user -> getDefautPaymentMethod()-> paypal_email}}</div>
				@else
					<div><strong>Card: </strong>{{$user -> getDefautPaymentMethod()-> card_brand}} **** **** **** {{$user -> getDefautPaymentMethod()-> card_last_four}}</div>
				@endif

А теперь хотелось бы для каждого юзера в списке всех методов метод по умолчанию выделить. Как это красиво сделать?

Вот цикл

@foreach($user->payment_methods()->get() as $payment_method)
				@if($payment_method ->type == PaymentMethod::PAYPAL_ACCOUNT)
					<div><strong>Paypal: </strong> {{$payment_method -> paypal_email}}</div>
				@else
					<div><strong>Card: </strong>{{ $payment_method -> card_brand }} **** **** **** {{$payment_method -> card_last_four}}</div>
				@endif
			@endforeach

Причем подзапроса который бы выполнялся в каждой итерации цикла не хотелось бы как и DB::RAW. Думаю в PaymentMethod нужно метод добавить который будет использовать отношение с User и проверять существует ли объект по этому отношению. Или когда беру все платежные методы $user->payment_methods()->get()  как то взять их with user по отношению через PaymentMethod.id = User.default_payment_method_id

Не в сети

#2 16.11.2017 08:41:11

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Ты перемудрил со структурой, здесь по идее надо многие-ко-многим и хранить значение по умолчанию в pivot таблице.

  1. как то взять их with user по отношению через PaymentMethod.id = User.default_payment_method_id

Почему бы не сравнить так:

PHP
if ($payment_method->id === $user->default_payment_method_id)

Не в сети

#3 16.11.2017 09:54:43

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

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

Так, что структуру кажется я верно отразил.

if ($payment_method->id === $user->default_payment_method_id)

Логично тк юзер то известен для данной таблицы! Надо было так и сделать. Но я сделал так:

                       <h3>Payment methods</h3>
			<table border="1">
			<tr><th></th><th></th><th></th></tr>
			@foreach($user->payment_methods()->withCount('defaultForUser')->get() as $payment_method) 
				<tr>
				<td>
					<div><strong>id:</strong> {{$payment_method->id }}</div>
					<div><strong>gateway id:</strong> {{ $payment_method->braintree_id }}</div>
				</td>
				<td>
					@if($payment_method ->type == PaymentMethod::PAYPAL_ACCOUNT)
						<div><strong>Paypal: </strong> {{$payment_method -> paypal_email}}</div>
					@else
						<div><strong>Card: </strong>{{ $payment_method -> card_brand }} **** **** **** {{$payment_method -> card_last_four}}</div>
					@endif
					
				<td>
					@if ($payment_method -> default_for_user_count)
						<strong style = "color:green">Default</strong>
					@endif
				</td>
				</td>
				</tr>
			@endforeach
			</table>

Не в сети

#4 16.11.2017 09:57:17

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

 $user->payment_methods()->withCount('defaultForUser')->get()

Кстати, а вот такие штуки в шаблонах надо ли прятать в метод модели в данном случае PaymentMethod ?

Что бы было типа $user->payment_methods()->getWirhDefaultForUser();

Не в сети

#5 16.11.2017 09:59:43

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

А на стаке мне вот что предложили:

@foreach ($user->payment-methods()->get() as $payment_method)
    <div>
        @if ($user->get_default_payment_method()->id === $payment_method->id)
            <strong>Default Payment Method</strong>
        @endif
        {{ $payment_method }}
    </div>
@endforeach

По сути тоже самое что ты.. Сравнивать прямо в цикле.

Не в сети

#6 16.11.2017 10:14:09

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Кстати, а вот такие штуки в шаблонах надо ли прятать в метод модели в данном случае PaymentMethod ?

В шаблонах вообще запросов не должно быть. Нужно заранее подгружать все данные и работать с результатом в представлении.

Многие по многим не пойдет. Ведь один платеж может быть только одной картой.

Я говорю об отношении между User и PaymentType.

По сути тоже самое что ты.. Сравнивать прямо в цикле.

Это создаст дополнительно N запросов, мой код нет.

Не в сети

#7 16.11.2017 12:29:16

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

В шаблонах вообще запросов не должно быть. Нужно заранее подгружать все данные и работать с результатом в представлении.

Хорошо, а как верно эту eloquent конструкцию спрятать? В контроллер не хочу, нет смысла.. он должен быть тонкий да и вообще контроллеров много,  а суть запроса одна. Почему бы не спрятать в модель?

Я говорю об отношении между User и PaymentType.

А оно не многое ко многим. Данный PaymentType не может принадлежать более чем одному юзеру

Это создаст дополнительно N запросов, мой код нет.

Да тк ты обращаешся просто к полю user

Не в сети

#8 16.11.2017 12:31:35

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Главный сейчас вопрос

$user->payment_methods()->withCount('defaultForUser')->get()

И другие подобные конструкции в views верно ли было бы спрятать в методы модели? И что бы в views обращаться через $user и его отношения к данным методом моделей?

Не в сети

#9 16.11.2017 14:25:03

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Почему бы не спрятать в модель?

Да, помести в модель. Контроллер передаст загруженные данные в представление.

Да тк ты обращаешся просто к полю user

А разве $user->default_payment_method_id вернет не тоже самое, что $user->get_default_payment_method()->id?

Не в сети

#10 16.11.2017 14:43:12

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

А разве $user->default_payment_method_id вернет не тоже самое, что $user->get_default_payment_method()->id?

не тоже самое $user->get_default_payment_method() вернет для юзера сам дефолтный пеймент метод, а то что ты указал вернет лишь строку с его id


Вот я думаю а как лучше поместить в модель... Чуть позже пришлю как сделал

Не в сети

#11 16.11.2017 14:49:37

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Отладил таким образом

class User extends Authenticatable
{
 ....
	public function payment_methods()
    {
        return $this->hasMany(AppPaymentMethod::class, $this->getForeignKey())->orderBy('created_at', 'desc');
    }
	
	public function getPaymentMethods()
	{
		return $this->payment_methods()->withCount('defaultForUser')->get();
	}
  ....
}


class PaymentMethod extends Model
{
    ...  
	
	public function defaultForUser()
    {
        return $this->belongsTo(User::class, 'id', 'default_payment_method_id');
    }
	...
}

@foreach($user->getPaymentMethods() as $payment_method) 

@endforeach

Конечно было бы наверное правильнее в PaymentMethod добавить метод который возвращает withCount('defaultForUser')->get(); и еще его вызывать в User через отношение... Но вроде и так норм..

А Scope в таких примерах никак не применимы?

Скажем для withCount('defaultForUser') и уже вызывать PaymentMethod с его использованием...

Изменено htclog81 (16.11.2017 14:52:30)

Не в сети

#12 16.11.2017 15:13:55

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

не тоже самое $user->get_default_payment_method() вернет для юзера сам дефолтный пеймент метод, а то что ты указал вернет лишь строку с его id

Но ты же делаешь $user->get_default_payment_method()->id а не $user->get_default_payment_method(). Т.е. просто так исполняешь кучу лишних запросов в БД. Установи Laravel Debugbar, чтобы наблюдать за запросами.

Не в сети

#13 16.11.2017 15:29:37

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Но ты же делаешь $user->get_default_payment_method()->id а не $user->get_default_payment_method(). Т.е. просто так исполняешь кучу лишних запросов в БД. Установи Laravel Debugbar, чтобы наблюдать за запросами.


Я знаю понимаю. Я этот метод вроде бы и не использую в итоге? Посмотри, что я в итоге прислал... Debugbar скоро установлю. Я его удалил тк он мне портил ответы WebHook его js скрипты ответ сервера засоряли.

Не в сети

#14 16.11.2017 22:05:21

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Включил я в итоге debug bar

Запрос получился вот таким:


select `payment_method`.*, (select count(*) from `users` where `payment_method`.`id` = `users`.`default_payment_method_id`) as `default_for_user_count` from `payment_method` where `payment_method`.`user_id` = '1' and `payment_method`.`user_id` is not null and `payment_method`.`deleted_at` is null order by `created_at` desc


Помойму вполне терпимо. Я и на голом php так писал. Это же в не цикле подзапросы. select count со связкой по ключу норм

Не в сети

#15 16.11.2017 22:09:36

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Это при использовании $user->get_default_payment_method()->id?

Не в сети

#16 16.11.2017 23:16:21

htclog81
Откуда: Москва
Сообщений: 192
Сайт

Re: Eloquent красиво выбрать есть ли для каждого объекта связаная запись

Нет.

Это вот

class User extends Authenticatable
{
 ....
	public function payment_methods()
    {
        return $this->hasMany(AppPaymentMethod::class, $this->getForeignKey())->orderBy('created_at', 'desc');
    }
	
	public function getPaymentMethods()
	{
		return $this->payment_methods()->withCount('defaultForUser')->get();
	}
  ....
}


class PaymentMethod extends Model
{
    ...  
	
	public function defaultForUser()
    {
        return $this->belongsTo(User::class, 'id', 'default_payment_method_id');
    }
	...
}

@foreach($user->getPaymentMethods() as $payment_method) 

@endforeach

Не в сети

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