Laravel 5.4:如何在不将一个foreach嵌入另一个foreach的情况下获得相同的结果?

时间:2018-04-05 14:20:13

标签: php laravel laravel-5 foreach

我打算以特定语言$new获取新闻if($new_lang->lang_id == $lang_id),然后查看与新闻相对应的图片@foreach($data['images'] as $image)表,其中主要是@if($image->imageable_id == $new->id && $image->main == '1')

问题在于,当嵌套foreach时,负载非常慢,因为它们有超过7,000条新闻和大约70,000张图像。

如果不将一个foreach嵌入另一个foreach中,如何获得相同的结果?

@foreach($new->langs as $new_lang)
@if($new_lang->lang_id == $lang_id)
    @foreach($data['images'] as $image)
        @if($image->imageable_id == $new->id && $image->main == '1')
            @php
                $mini = substr($image->path, 0, strrpos( $image->path, "/"));
                $name = substr($image->path, strrpos($image->path, '/') + 1);
                $image_mini = $mini.'/mini-'.$name;
            @endphp
            <div class="crop">
                <a href="{{ route('noticia', [$new->id, $new_lang->slug]) }}">{{ HTML::image(file_exists($image_mini)? $image_mini : $image->path, '', array('class' => 'img-responsive ancho_100')) }}</a>
            </div>
        @endif
    @endforeach
@endif
@endforeach

已编辑:添加了控制器和特征

这是控制器:

    use ListNoticias;

public function actualidad(Request $request)
{
    $data['section_id'] = explode(',', '1,2,3');

    $data['ruta'] = 'actualidad';
    $data['title'] = __('header.actualidad');
    $data['num'] = $request->num;
    $url = $request->url();

    $data = $this->listNoticias($data, $url);

    return view('web.actualidad.listado', compact('data'));
}

特质:

trait ListNoticias
{
public function listNoticias($data, $url)
{
    $now = date('Y-m-d');
    $time = date('H:i:s');

    (isset($data['num']))? $num = $data['num'] : $num = '15';

    $data['images'] = Image::where('imageable_type', 'App\Models\Noticia')->get();
    $data['sections']  = Section::all();

    $data['noticias'] = Noticia::with('langs')->where('date', '<', $now)
        ->where('active', '1')
        ->whereIn('section_id', $data['section_id'])
        ->orWhere('date', '=', $now)
        ->where('time', '<=', $time)
        ->where('active', '1')
        ->whereIn('section_id', $data['section_id'])
        ->orderBy('date', 'desc')
        ->orderBy('time', 'desc')
        ->get();

    $data['noticias-es'] = [];
    $data['noticias-en'] = [];
    $data['noticias-pt'] = [];

    foreach($data['noticias'] as $row){
        foreach($row->langs as $row_lang) {
            if ($row_lang->lang_id == '1') {
                $data['noticias-es'][] = $row;
            } elseif ($row_lang->lang_id == '2') {
                $data['noticias-en'][] = $row;
            } elseif ($row_lang->lang_id == '3') {
                $data['noticias-pt'][] = $row;
            } else null;

        }
    }

    // Manual paginate
    /*  Get current page form url e.x. &page=1
        Create a new Laravel collection from the array data
        Slice the collection to get the items to display in current page
        Create our paginator and pass it to the view
        set url path for generated links
    */

    $currentPage = LengthAwarePaginator::resolveCurrentPage();

    // ES
    $itemCollection = collect($data['noticias-es']);
    $currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
    $data['noticias-es'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
    $data['noticias-es']->setPath($url);

    // EN
    $itemCollection = collect($data['noticias-en']);
    $currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
    $data['noticias-en'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
    $data['noticias-en']->setPath($url);

    // PT
    $itemCollection = collect($data['noticias-pt']);
    $currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
    $data['noticias-pt'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
    $data['noticias-pt']->setPath($url);

    return $data;

}
}

已编辑2

第一个视图是部分视图,并且包含在此视图中,其中包含另外两个foreach nestead。这是因为我需要以三分之一显示记录。我已经对其他包含对问题不必要的内容进行了评论:

@foreach ($data['noticias-'.$lang]->chunk(3) as $chunk)

<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 pad_inf_2">

    @foreach($chunk as $key => $new)
        <div class="col-xs-12 col-sm-12 col-md-4 col-lg-4 pad_der_1">
            <article>
                //@include('web.index.partials.noticia_etiqueta')
                @include('web.actualidad.partials.noticia_image_listado')
                //@include('web.index.partials.noticia_date_section')
                //@include('web.index.partials.noticia_title')
            </article>
        </div>
    @endforeach

</div>
@endforeach

Model Noticia:

class Noticia extends Model
{
protected $fillable = ['section_id', 'active', 'date', 'time', 'author'];

public function images()
{
    return $this->morphMany('App\Models\Image', 'imageable');
}

public function tags()
{
    return $this->morphToMany('App\Models\Tag', 'taggable');
}

public function section()
{
    return $this->belongsTo('App\Models\Section');
}

public function langs()
{
    return $this->hasMany('App\Models\LangNoticia');
}

}

LangNoticia模特:

class LangNoticia extends Model
{
protected $fillable = ['noticia_id', 'lang_id', 'title', 'lead', 'text', 'author_place', 'slug'];

public function noticia()
{
    return $this->belongsTo('App\Models\Noticia');
}
}

已编辑3 :使用 abr

的建议减少特征中的代码

特质减少:

trait ListNoticias
{
public function listNoticias($data, $url)
{
    $lang = Session::get('lang');
    if($lang == 'en') $lang_id = '2';
    elseif($lang == 'pt') $lang_id = '3';
    else $lang_id = '1';

    $now = date('Y-m-d');
    $time = date('H:i:s');

    (isset($data['num']))? $num = $data['num'] : $num = '15';

    $data['sections']  = Section::all();
    $data['images'] = Image::where('imageable_type', 'App\Models\Noticia')->where('main', true)->get();

    $noticias = Noticia::with('langs')
        ->where('date', '<', $now)
        ->where('active', '1')
        ->whereIn('section_id', $data['section_id'])
        ->orWhere('date', '=', $now)
        ->where('time', '<=', $time)
        ->where('active', '1')
        ->whereIn('section_id', $data['section_id'])
        ->orderBy('date', 'desc')
        ->orderBy('time', 'desc')
        ->get();

    $data['noticias-'.$lang] = [];

    foreach($noticias as $row){
        foreach($row->langs as $row_lang) {
            if ($row_lang->lang_id == $lang_id) {
                $data['noticias-'.$lang][] = $row;
            }
        }
    }

    // Paginate
    /*  Get current page form url e.x. &page=1
        Create a new Laravel collection from the array data
        Slice the collection to get the items to display in current page
        Create our paginator and pass it to the view
        set url path for generated links
    */

    $currentPage = LengthAwarePaginator::resolveCurrentPage();

    $itemCollection = collect($data['noticias-'.$lang]);
    $currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
    $data['noticias-'.$lang] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
    $data['noticias-'.$lang]->setPath($url);

    return $data;

}
}

2 个答案:

答案 0 :(得分:1)

只是分享一些想法。从检索Noticias

的内容开始

你真的需要所有这三种语言吗?未来还会有更多吗?它只是这些&#39; Noticias&#39;中的一部分。你要表现出来的?

回答这些问题你应该提出:

  1. 一种方法,在添加另一种语言的情况下,您不必改变该功能;

  2. 一种提高查询性能的方法,因为获取7k记录与记录7k x 154种语言不同

  3. 你真的需要所有7k吗?

  4. 例如,您可以将该查询拆分为多个(如果您确实需要所有语言),或者在您的Eager Loading语句中使用高级功能对其进行验证。

    Noticia::with(['langs' => function ($query) {
        //where this lang_id == 1, select it as whatever name you require
    }])
    .
    .
    .
    

    其次,你确定你想要的是这些人究竟是怎么回事吗?您没有使用orWhere或Where过滤太多内容。如果您打算缩小搜索范围,我建议在where语句中使用函数,就像上面显示的示例一样。 如果您想查看查询的全部内容,请写下:

    dd($data['noticias']->toSql());
    

    如果你声称拥有超过7k的7k新闻,那么获取所有这些新闻的图像将会很沉重。您不需要在一开始就真正加载所有内容,您可以使用简单的->paginate(15);缩小范围,并在->skip(15);旁边

答案 1 :(得分:1)

我认为你可以像这样访问新闻主图像:

$new_lang->noticia->images()->where('main', true)->first();

您可以在LangNoticia模型中定义accessor以方便通话。

public function getImageAttribute()
{
    return $this->noticia->images()->where('main', true)->first();
}

在您的视图中,您可以删除foreach()并改为调用访问者。

@foreach($new->langs as $new_lang)
    @if($new_lang->lang_id == $lang_id)
        @php
            $mini = substr($new_lang->image->path, 0, strrpos( $new_lang->image->path, "/"));
            $name = substr($new_lang->image->path, strrpos($new_lang->image->path, '/') + 1);
            $image_mini = $mini.'/mini-'.$name;
        @endphp
        <div class="crop">
            <a href="{{ route('noticia', [$new->id, $new_lang->slug]) }}">{{ HTML::image(file_exists($image_mini)? $image_mini : $new_lang->image->path, '', array('class' => 'img-responsive ancho_100')) }}</a>
        </div>
    @endif
@endforeach

现在,您可以删除对特征中Image模型的调用。

在查询Noticia模型时使用eager loading以减少查询次数。

Noticia::with(['images' => function($query) {
    $query->where('main', true);
}])->get();