Идиоматический c способ написания длинных запросов в Laravel? - PullRequest
1 голос
/ 11 февраля 2020

Я совершенно новичок в Laravel. Недавно я написал запрос к базе данных, который отлично работает и выглядит следующим образом:

function search($keywords, $lang)
{
if(count($keywords)<1)
  return [];

$pages = DB::table('pages')
  ->select(explode(',','pages.id,pages.updated_at,pages.created_at,page_translations.title,page_translations.description'))
  ->selectSub(function($query){
    $query->selectRaw("'pages'");
  },'content_type')
  ->join('page_translations','page_translations.page_id','=','pages.id')
  ->whereNull('pages.deleted_at')
  ->whereNull('page_translations.deleted_at')
  ->where([
    ['pages.published','=',1],
    ['page_translations.locale','=',$lang],
    ['page_translations.active','=',1],
  ])
  ->where(function($query) use ($keywords) {

    // Title
    $query->where(function($subquery) use ($keywords) {
      $subquery->where('page_translations.title','LIKE','%'.$keywords[0].'%');
      for($c=1;$c<count($keywords); $c++)
        $subquery->where('page_translations.title','LIKE','%'.$keywords[$c].'%');
    });
    // Description
    $query->orWhere(function($subquery) use ($keywords) {
      $subquery->where('page_translations.description','LIKE','%'.$keywords[0].'%');
      for($c=1;$c<count($keywords); $c++)
        $subquery->where('page_translations.description','LIKE','%'.$keywords[$c].'%');
    });
    // Block Content
    $query->orWhereIn('pages.id',function($subquery) use ($keywords) {
        $subquery->select('blockable_id')
          ->from('blocks')
          ->where('blockable_type','=','App\\\\Models\\\\Page')
          ->where(function($blockquery) use ($keywords) {
            $blockquery->where('content','LIKE','%'.$keywords[0].'%');
            for($c=1;$c<count($keywords);$c++)
              $blockquery->where('content','LIKE','%'.$keywords[$c].'%');
          });
      });
  });
$articles = DB::table('articles')
  ->select(explode(',','articles.id,articles.updated_at,articles.created_at,article_translations.title,article_translations.description'))
  ->selectSub(function($query){
    $query->selectRaw("'articles'");
  },'content_type')
  ->join('article_translations','article_translations.article_id','=','articles.id')
  ->whereNull('articles.deleted_at')
  ->whereNull('article_translations.deleted_at')
  ->where([
    ['articles.published','=',1],
    ['article_translations.locale','=',$lang],
    ['article_translations.active','=',1],
  ])
  ->where(function($query) use ($keywords) {

    // Title
    $query->where(function($subquery) use ($keywords) {
      $subquery->where('article_translations.title','LIKE','%'.$keywords[0].'%');
      for($c=1;$c<count($keywords); $c++)
        $subquery->where('article_translations.title','LIKE','%'.$keywords[$c].'%');
    });
    // Description
    $query->orWhere(function($subquery) use ($keywords) {
      $subquery->where('article_translations.description','LIKE','%'.$keywords[0].'%');
      for($c=1;$c<count($keywords); $c++)
        $subquery->where('article_translations.description','LIKE','%'.$keywords[$c].'%');
    });
    // Block Content
    $query->orWhereIn('articles.id',function($subquery) use ($keywords) {
        $subquery->select('blockable_id')
          ->from('blocks')
          ->where('blockable_type','=','App\\\\Models\\\\Article')
          ->where(function($blockquery) use ($keywords) {
            $blockquery->where('content','LIKE','%'.$keywords[0].'%');
            for($c=1;$c<count($keywords);$c++)
              $blockquery->where('content','LIKE','%'.$keywords[$c].'%');
          });
      });
  })
  ->union($pages)
  ->orderBy('updated_at','desc')
  ->get();

  return $articles->toArray();
  }

В основном эта функция получает массив $keywords. Затем запрос будет искать любые page или любые article, имеющие ALL ключевые слова в title, description or content в активном translation tables or block tables. Я использую UNION, чтобы помочь мне объединить результаты двух разных типов контента и упорядочить их по времени последнего обновления.

Когда я смотрю на этот код, он НЕ выглядит как идиоматический c laravel способ делать вещи. Как будто я только что написал Raw SQL. Кто-нибудь может подсказать, как правильно использовать Laravel / Eloquent для выполнения вышеуказанных функций?

1 Ответ

2 голосов
/ 11 февраля 2020

Я предпочитаю использовать QueryBuilder для сложных SQLQueries и Eloquent для более случайных вещей. Особенно, если он уже работает, я бы оставил его в QueryBuilder вместо того, чтобы тратить время на перенос кода в Eloquent.

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

 $subquery->where('page_translations.title','LIKE','%'.$keywords[0].'%');
 for($c=1;$c<count($keywords); $c++)
     $subquery->where('page_translations.title','LIKE','%'.$keywords[$c].'%');

Вы можете просто переписать ее как

for($c=0;$c<count($keywords); $c++)
  $subquery->where('page_translations.title','LIKE','%'.$keywords[$c].'%');

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

select(['pages.id', 'pages.updated_at', ...]);

Знак = обычно можно опустить:

->where('articles.published', 1)

Так что мой вывод таков: если вы просто оптимизируете этот код, все будет хорошо и идиоматически c .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...