Передать красноречивое сводное значение ресурсу Laravel API

#php #laravel #api #eloquent #pivot-table

#php #laravel #API #eloquent #сводная таблица

Вопрос:

Laravel 5.7. У меня есть 2 модели Eloquent: Owner, Cat.

Модель владельца:

 public function cats()
{
    return $this->belongsToMany('AppCat')->withPivot('borrowed');
}
  

Модель Cat:

 public function owners()
{
    return $this->belongsToMany('AppOwner')->withPivot('borrowed');
}
  

cat_owner Сводная таблица содержит эти поля:

 id | cat_id | owner_id | borrowed
---------------------------------
1  |   3    |   2      |   1
  

Я хочу, чтобы мой API возвращал список всех cats, и если авторизованный пользователь позаимствовал этот cat, я хочу, чтобы для borrowed поля было установлено значение true. Это то, что у меня есть на данный момент:

Контроллер:

 public function index()
{
    return CatResource::collection(Cat::all());
}
  

CatResource:

 public function toArray()
{
    $data = ['id' => $this->id, 'borrowed' => false];
    $owner = auth()->user();
    $ownerCat = $owner->cats()->where('cat_id', $this->id)->first();
    if ($ownerCat) {
        $data['borrowed'] = $ownerCat->pivot->borrowed == 1 ? true : false;
    }
    return $data;
}
  

Это работает, но кажется расточительным запрашивать $owner для каждого cat, например, если в базе данных 5000 cats. Есть ли более эффективный способ сделать это? Я могу придумать 2 возможных способа:

  1. Передать $owner в CatResource (требуется переопределение существующего ресурса коллекции).

  2. Сначала получите эту информацию в контроллере, прежде чем передавать в CatResource .

Я предпочитаю второй способ, но не вижу, как это сделать.

Комментарии:

1. Если для cat и зарегистрированного владельца вообще нет сводной записи, результат не должен содержать borrowed поле?

Ответ №1:

Попробуйте условную связь.

 public function toArray($request)
{
    return [
      'id' => $this->id,
      'borrowed' => false,
      'borrowed' => $this->whenPivotLoaded('cat_owner', function () {
         return $this->owner_id === auth()->id() amp;amp; $this->pivot->borrowed == 1 ? true : false;
      })
   ];
}
  

затем вызовите return CatResource::collection(Cat::with('owners')->get());

Комментарии:

1. Там нет логики сравнивать владельца cat с зарегистрированным владельцем.

2. Спасибо за настойчивость. К сожалению, borrowed ключ не доставляется. Я предполагаю, что whenPivotLoading возвращается false . Я использую with('owners') , поэтому я не уверен, что здесь происходит не так.

3. @GluePear вы решили эту проблему? Я борюсь с точно такой же проблемой.

Ответ №2:

Вы правы, это создает большую дополнительную нагрузку. Я думаю, вы сталкиваетесь с ограничением, заключающимся в том, что вы не можете знать, какая форма записи cat_owner вам нужна, пока не узнаете обе записи, которые вы используете, из таблицы cat и owner.

Для всех, кто все еще заинтересован, моим решением было бы создать ресурс, который выдает вам только значения pivot

Поскольку следующее возвращает коллекцию, вы не можете перейти к сводной таблице по ней:

 /*
* returns a collection
*/
$data['borrowed'] = $this->owners

/*
 * So this doesNOT work. Since you can’t get the pivot
 * data on a collection, only on a single record
 */
$data['borrowed'] = $this->owners->pivot

  

Вы должны получить коллекцию, а затем вы сможете прочитать сводные данные в Ресурсе записи владельца. Если этот ресурс предназначен только для сводных данных, я бы назвал его чем-то вроде атрибутов.

создайте новый ресурс для атрибутов, что-то вроде:

 class CatOwnerAttributeResource extends JsonResource
{
    public function toArray($request)
    {
      return [
          'borrowed' => $this->pivot->borrowed,
        ];
    }
}
  

Затем получите коллекцию следующим образом:

 $data = ['id' => $this->id, 'borrowed' => false];

/*
 * Get the collection of attributes and grab the first (and only) record. 
 * NOTE: the filtering is done in the collection, not in the DBM. If there
 * is a possibility that the collection of owners who own this cat gets really 
 * big this is not the way to go! 
 */
if ($attributes =
  CatOwnerAttributeResource::collection(
    $this->owner
          ->where(‘id’ = $auth->user()->id)
          ->first() 
) {

  $data[‘borrowed’] = $attributes->borrowed == 1 ? true : false;
}
return $data;
  

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