За последние 24 часа нас посетили 6310 программистов и 496 роботов. Сейчас ищут 133 программиста ...

Насколько оптимально я использовал связи и как сделали бы вы

Тема в разделе "Laravel", создана пользователем rewuxiin, 25 окт 2019.

  1. rewuxiin

    rewuxiin Активный пользователь

    С нами с:
    17 апр 2012
    Сообщения:
    603
    Симпатии:
    87
    Задумал я сделать фильтры с параметрами. Но это не магазин, а просто портал с новостями, постами которые будут привязываться к этим параметрам.
    Подбор будет происходить через get запрос, потому для красивых ссылок будем передавать не id параметров(что конечно было бы намного проще), а названия параметров
    Схема условно вот такая - некоторые не важные, на данный момент, поля убрал для читаемости:

    PHP:
    1. class Filter extends Model
    2. {
    3.     protected $fillable = [
    4.         'name',
    5.         'slug',
    6.     ];
    7. }
    8.  
    9. class FilterOption extends Model
    10. {
    11.     protected $fillable = [      
    12.         'filter_id',
    13.         'value'//Название параметра      
    14.     ];
    15. }
    16.  
    17. class FilterOptionRelation extends Model
    18. {  
    19.     protected $fillable = [      
    20.         'model_type', //Здесь хранится модель в виде строки, например App\Models\Post
    21.         'model_id', //а здесь id модели
    22.         'option_id',
    23.     ];
    24. }
    и вот у нас контроллер в котором происходит поиск по названию опции, а не по ее id.
    в данном случае я решил сделать посадочную страницу для страницы с параметром Город, потому параметр запроса нам заранее известен.

    PHP:
    1. $result =  FilterOption::where('value, $request->city_name)
    2.                              ->with('filter_option_relation')//Делаю жадную загрузку
    3.                              ->first();
    Вот метод для жадной загрузки
    PHP:
    1. class FilterOption extends Model
    2. {
    3.     public function filter_option_relation(){
    4.         return $this->hasMany('App\Models\FilterOptionRelation', 'option_id', 'id');
    5.     }
    6. }

    Теперь в модели создаем метод подтягивания модели исходя из поля model_type

    PHP:
    1. class FilterOptionRelation extends Model
    2. {
    3.     public function model_data(){
    4.         return $this->belongsTo($this->model_type, 'model_id', 'id');
    5.     }
    6. }
    и уже в контроллере или в шаблоне blade мы можем обратиться к модели

    PHP:
    1. foreach ($result->filter_option_relation as $options){
    2.           echo $options->model_data->name;
    3. }
    подскажите перемудрил или недомудрил и можно сделать оптимальнее?
    хотелось бы для развития понимания делать правильно.

    заранее спасибо.
     
  2. rewuxiin

    rewuxiin Активный пользователь

    С нами с:
    17 апр 2012
    Сообщения:
    603
    Симпатии:
    87
    Ах да, забыл, не могу пока никак найти, каким образом при вызове ->with('filter_option_relation') передать еще параметр для выборки определенной модели
     
  3. rewuxiin

    rewuxiin Активный пользователь

    С нами с:
    17 апр 2012
    Сообщения:
    603
    Симпатии:
    87
    итак, после более глубокого изучения доки я переделал свою выборку.
    однако сталкиваюсь с проблемой - как выбрать из базы данные которые строго подходят под все параметры из массива whereIn, а не выбираются все

    т.е. о чем это я

    снова имеем три таблицы
    Posts
    FilterOptions
    FilterOptionRelations

    понятно что в таблице FilterOptions хранятся опции
    например

    id, slug
    1, minsk
    2, moskva
    3, washington

    понятно что в таблице FilterOptionRelations хранятся связи
    например
    model_type, model_id, option_id
    App\Post, 1, 1
    App\Post, 1, 3
    App\Post, 3, 1
    PHP:
    1. //Здесь у нас связь
    2. public function filter_options() {
    3.     return $this->morphToMany('App\Models\FilterOption', 'filter_option_relation');
    4. }
    Выбираю c параметром slug - minsk
    PHP:
    1. Post::whereHas('filter_options', function ($query){
    2.                    return $query->whereIn('filter_options.slug', ['minsk']);
    3.                 });
    Получаю коллекцию с двумя постами, в данном случае все верно.

    Теперь выбираю c параметром slug - ['minsk', 'washington']
    PHP:
    1. Post::whereHas('filter_options', function ($query){
    2.                    return $query->whereIn('filter_options.slug',['minsk', 'washington']);
    3.                 });
    И получаю снова два поста, т.к. к одному и второму подошел slug - minsk
    А хочу получить только один пост, к которому подходят оба параметра minsk и washington, а пост которому подходит только minsk - отбросить.

    вроде уже вдоль и поперек перелистал доку и примеров в гугле не нашел.
     
  4. Sail

    Sail Активный пользователь

    С нами с:
    1 ноя 2016
    Сообщения:
    1.193
    Симпатии:
    261
    В этом случае надо использовать цепочку where(), а не whereIn().
    Подробнее, в пункте "Сложная фильтрация (WHERE)"
     
  5. rewuxiin

    rewuxiin Активный пользователь

    С нами с:
    17 апр 2012
    Сообщения:
    603
    Симпатии:
    87
    В моем случае where не подходит.
    Потому я сделал именно whereIn.
     
  6. Sail

    Sail Активный пользователь

    С нами с:
    1 ноя 2016
    Сообщения:
    1.193
    Симпатии:
    261
    Можно усилить ограничения добавлением группировки (group by) и сравнением (having) количества полученных совпадений с количеством элементов массива.
     
  7. rewuxiin

    rewuxiin Активный пользователь

    С нами с:
    17 апр 2012
    Сообщения:
    603
    Симпатии:
    87
    да, так и сделал.

    PHP:
    1. $filterOptions = ['minsk', 'moskva'];
    2. Post::whereHas('filter_options', function ($query) use ($filterOptions){
    3.                                            return $query->whereIn('filter_options.slug', $filterOptions);
    4.                                         }, '=', count($filterOptions)
    5.                         )
    6.                         ->where('enabled', true)