Загрузка Eloquent связей используя left join вместо дополнительных запросов

    Если вы когда-либо пытались выполнить следующий код для загрузки данных из вашей Eloquent модели и получали ошибку:

    MyModel::with('relation')->where('relation.title', '=', $title)->orderBy('relation.field', 'asc')
    

    То вы наверняка знаете, что Eloquent производит загрузку связанных данных отдельным запросом и ни фильтрация, ни сортировки по ним работать не будут.

    В таком случае вам возможно поможет моя библиотека, которая позволяет перенести загрузку BelongsTo связей из дополнительных запросов в основной запрос с left join`ами.

    Как это использовать?


    После установки пакета необходимо добавить трэйт в описание вашей модели:

    class MyModel extends \Eloquent
    {
    	use \SleepingOwl\WithJoin\WithJoinTrait;
    }
    

    И все, теперь вам доступна пара новых методов:

    includes($relations)


    Используйте этот метод вместо Model::with(), чтобы все BelongsTo связи были сконвертированы в left join`ы (поведение загрузки связей других типов изменено не будет).

    StreetImage::includes('street')->where('street.id', '<', 10)->get()
    

    Вместо двух привычных запросов будет выполнен один (<…> в запросе — это все поля связанной таблицы):

    select 
    	`street`.`<…>` as `__f__street---<…>`, 
    	`street_images`.* 
    from 
    	`street_images` 
    left join 
    	`streets` as `street` on `street`.`id` = `street_images`.`street_id` 
    where
    	`street`.`id` < 10
    

    references($relations)


    Этот метод позволяет комбинировать функционал Eloquent по умолчанию с функционалом моей библиотеки:

    MyModel::with('relation', 'relation2')->references('relation')->...
    

    В данном случае «relation2» будет загружен дополнительным запросом, а «relation» — с использование left join.

    Исходный код на GitHub
    • +13
    • 12.8k
    • 8
    Share post

    Comments 8

      0
      А вот на примере таблиц habrahabr.ru/post/229905/
      как вывести product соеденив все 4 таблицы (товар, категорию товара, имена фильтров и их значения)
        +1
        Product::includes('category', 'options')
        

        где category — BelongsTo связь и будет загружена через джоин, options — HasMany связь и будет загружена отдельными запросами
        То есть в запросе вы сможете опираться на поля продукта и категории, но не опций.
        0
        То вы наверняка знаете, что Eloquent производит загрузку связанных данных отдельным запросом и ни фильтрация, ни сортировки по ним работать не будут.


        Ну, вообще-то, фильтровать можно:

        $objects = Model::whereHas('relation', function($query) use ($filter)
        {
            $query->whereFilter($filter);
        })->get();
        


        А насчет «упорядочить», это нужно подумать… возможно тоже что-то есть.
          0
          возможно даже это пройдет:
           $query->whereFilter($filter)->orderBy('order');
          
          но я не берусь утверждать.
            0
            Если посмотреть на генерируемый запрос, то это не совсем то. Eloquent использует подзапрос для подсчета количества связанных записей, удовлетворяющих условию, соответственно с загрузкой это ничего общего не имеет. То есть по сути будет произведено целых два с половиной запроса (основной, в котором будет подзапрос считающий количество по фильтру, и запрос на загрузку связанных данных).

            В моем же случае все это укладывается в один запрос, плюс на мой взгляд код чище.
              0
              Я и не говорил о количестве запросов. Я говорил о возможности фильтровать результат выборки по связанной модели.
                0
                В любом случае, join моделей будет полезен. Это как раз то, чего мне не хватало в первое время, когда я только начал работать c Eloquent.
            0
            Спасибо, это действительно очень нужная функциональность.

            github.com/sleeping-owl/with-join/issues/14

            Only users with full accounts can post comments. Log in, please.