Присоединиться к подзапросу в модели Laravel - PullRequest
0 голосов
/ 02 мая 2020

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

Справочная информация

Я создаю базу данных comi c для личного использования, чтобы отслеживать мои чтения comi c. Каждый коми c принадлежит серии. У каждого комикса есть дата выхода. Дата выпуска серии является первым выпуском соответствующего коми c. У меня есть красноречивая функция seriesByDate() для этого:

class Series extends Model
{
  public $timestamps = false;
  protected $primaryKey = 'series_id';
  protected $fillable = ['series_name', 'publisher_id'];

  public function publisher()
  {
    return $this->belongsTo(Publisher::class, 'publisher_id');
  }

  public function comics()
  {
    return $this->hasMany(Comic::class, 'series_id', 'series_id');
  }

  // instead of saving the release date of a complete series
  // we look for the first comic in this series and get the
  // comic's release date.
  public static function seriesByDate()
  {
    $firstRelease = DB::table('comics')
      ->select('series_id', DB::raw('MIN(comic_release_date) as first_release'))
      ->groupBy('series_id');

    $seriesByDate = DB::table('series')
      ->leftJoinSub($firstRelease, 'first_release', function ($join) {
        $join->on('series.series_id', '=', 'first_release.series_id');
      })
      ->join('publishers', 'publishers.publisher_id', '=', 'series.publisher_id')
      ->select('series.series_id', 'series.series_name', 'first_release', 'publishers.publisher_name')
      ->get();

    return $seriesByDate;
  }

Что я хочу

Я хочу, чтобы release_date каким-то образом был постоянным для моей модели серии. Значение: когда я делаю App\Series::all(), я уже хочу иметь release_date в качестве столбца в моих возвращенных данных. Подобно App\Series::with('publishers')->get()

С моим решением, приведенным выше, я должен явно выполнить App\Series::seriesByDate()

Возможно ли это вообще? Можете ли вы дать мне подсказку?

Редактировать / обновить

Связанное видео @Musa показывает, как правильно сделать это в модели: { ссылка }

Ответы [ 2 ]

2 голосов
/ 02 мая 2020

вы можете определить аксессор , а затем добавить значение

class Series extends Model
{
    protected $appends = ['series_date'];

    public function getSeriesDateAttribute()
    {
        return self::seriesByDate();
        //OR build 'seriesByDate' manually, returning whatever you like.
    }
}
1 голос
/ 02 мая 2020
  • Вы не можете. Для этого нет волхвов c. В конечном итоге вы могли бы написать свой собственный Relation, но это было бы излишне сложно, просто для того, чтобы иметь красивый related / accessor. Оба решения не очень хороши с точки зрения производительности.

  • Не уверен, почему вы выбрали такую ​​структуру. Без какого-либо дополнительного контекста / объяснения, я настоятельно рекомендую вам иметь столбец release_date непосредственно внутри вашей модели Series. Это будет намного быстрее, чем ваша текущая структура.

  • Если вы все еще хотите придерживаться этой структуры, я бы лично получил базу данных release_date "php side" вместо "database" side ":

$series = App\Series::query()
    ->with([
        'publishers',
        'comics' => function ($query) {
            $query->orderBy('created_at');
        },
    ])
    ->get();

foreach ($series as $serie) {
    $serieTitle = $serie->title;
    $releaseDate = $serie->comics->first()->created_at;

    echo $serieTitle.' was first released '.$releaseDate->diffForHumans().'<br/>';
}

(не проверено), единственным недостатком является то, что он вернет коллекцию всех" комиксов ", которые имеет" ser ie ". Если у вас нет 10k комиксов на ser ie, и вы не загружаете 1k ser ie на страницу, это будет хорошо. В любом случае, это выглядит более элегантно и оптимизировано / быстрее, чем ваш метод seriesByDate.

edit: также, вы должны посмотреть «Advanced Querying With Eloquent» Джонатана Рейнинка, на Laracon 2018, я считаю. Он обсуждает подзапросы, как тот, который вам нужен. Я на 100% уверен, что вы найдете лучший и самый оптимальный подзапрос Eloquent, который вы можете создать для того, чего вы пытаетесь достичь: https://vimeo.com/showcase/7060635/video/255049572

...