Как применить красноречивое выражение Ларавеля » где » () к отношениям родителей и детей (хасМани ())

#laravel #eloquent #eloquent-relationship #laravel-eloquent-resource

Вопрос:

У меня есть Красноречивая модель Laravel под названием EmailList и модель под названием Subscriber . Я определил следующие отношения hasMany в списке рассылки.

     public function subscribers() {
        return $this->hasMany(Subscriber::class);
    }
 

Эти отношения прекрасно работают.

Теперь я хочу применить where() условие к родителю и where() условие к ребенку.

Например, у меня есть два EmailList s, со свойством id и свойством под названием listname («Список A» и «Список B»). Моя Subscriber модель имеет четыре соответствующих свойства: id , name , email и email_list_id .

Проблема: Я хочу взять всех подписчиков из определенного списка, например «Список А», и отфильтровать подписчиков с $query помощью (функция поиска с помощью Livewire).

Чтобы выполнить это, я придумал следующий код:

 EmailList::where('listname', $listname)->first()
                                    ->subscribers()
                                    ->where('name', 'LIKE', '%'.$this->search.'%')
                                    ->orWhere('email', 'LIKE', '%'.$this->search.'%')
                                    ->paginate(50) //or get(), if you like

 

Этот код частично сработал, потому что подписчики действительно были отфильтрованы, но не список рассылки. Другими словами, я просто искал всех подписчиков, а не подписчиков в определенном списке.

После долгих поисков я понял , что, возможно, мне следует использовать a whereHas() , но когда я попытался это сделать, он дал мне только списки рассылки, а не Подписчиков.

Было бы здорово, если бы кто-нибудь мог помочь мне отфильтровать это. Возможно, это мелочи для опытного разработчика,, но мне бы это очень помогло!

Обновление: Увидев ответ Эрикландхеера, я понял, что (конечно) У меня также есть идентификатор списка рассылки. Возможно, это немного упрощает задачу, так что вы можете использовать функцию find() вместо предложения where()->first (). (Я использую first (), потому что «имя списка» уникально, и мне нужен только один экземпляр. Это правильно?)

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

1. попробуйте этот $поиск=$это->поиск; $данные= список рассылки::где(‘имя списка’, $имя списка)->>с(‘подписчики’)->>>Где(‘подписчики’,функция ($запрос)использовать($поиск){ $запрос->>>>где(‘электронная почта’, ‘НРАВИТСЯ’, ‘%’.$поиск.’%’); })->>>>>где(функция ($запрос)использовать($поиск){ $запрос->>>>>>где(«имя», «НРАВИТСЯ», «%».$поиск.’%’) ->>>>>>>Или где(«электронная почта», «НРАВИТСЯ», «%».$поиск.’%’); })->>>>>>>>разбиение на страницы(50);

2. Эй, Джон, спасибо! Не могли бы вы, возможно, изложить это в ответе? Я проверю это и дам вам знать!

Ответ №1:

В настоящее время ваш код возвращает только первое EmailList . Вы сделали это намеренно, или вам нужен только первый EmailList , у которого есть $listname ?

Вот решение, использующее быструю загрузку с ограничениями, которое переопределяет subscribers связь с обратным вызовом. В результате должны быть все EmailLists $listname подписчики и соответствующие подписчики с поиском.

 EmailList::where('listname', $listname)
    ->with('subscribers', function($query) {
        return $query->where('name', 'LIKE', '%'.$this->search.'%')
            ->orWhere('email', 'LIKE', '%'.$this->search.'%')
    })->get();
 

Что касается обновления вопроса

Действительно, использование имени списка-это нормально. Если он уникален, никаких проблем. Вы можете использовать find() с идентификатором to, что предпочтительнее, если вы подумываете об изменении имени EmailList на более позднем этапе.

Возможно, более элегантным / читаемым решением было бы:

 $emailList = EmailList::firstWhere('listname', $listname);

$emailList->subscribers = $emailList->subscribers()
    ->where('name', 'LIKE', '%'.$this->search.'%')
    ->orWhere('email', 'LIKE', '%'.$this->search.'%')
    ->get();
 

Вы можете преобразовать поиск в модель / услугу, если используете его в большем количестве мест:

 // EmailList model

public function subscribers() 
{
    return $this->hasMany(Subscriber::class);
}

public function searchSubscribers(string $search): Collection 
{
    return $this->subscribers()
        ->where('name', 'LIKE', '%'.$this->search.'%')
        ->orWhere('email', 'LIKE', '%'.$this->search.'%')
        ->get();
}
 

Окончательный код

 $emailList = EmailList::firstWhere('listname', $listname);

$emailList->subscribers = $emailList->searchSubscribers($this->search);
 

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

1. Большое спасибо, Эрик, я проверю и дам тебе знать!

2. Я обновил свой первоначальный ответ, на мой взгляд, с более чистым подходом. Я не проверял это, но, по-моему, это довольно просто. Рассмотрите возможность рефакторинга search() функции в отдельный класс (например, UserSearchService), если функция также используется в других классах.

3. Эй, Эрик, большое спасибо за твой ответ. Я выбрал более чистое решение👍