Laravel 刀片表创建嵌套的 Foreach 循环

Posted

技术标签:

【中文标题】Laravel 刀片表创建嵌套的 Foreach 循环【英文标题】:Laravel Blade Table Creation Nested Foreach Loops 【发布时间】:2021-05-05 04:30:20 【问题描述】:

首先,我是编码和 Laravel 的新手,所以请放轻松。

我需要显示一个基于两个模型的表格:资源和事件。我在它们之间创建了一对多的关系。不幸的是,对于这张桌子,我有特殊的需求,这种关系根本没有帮助。事件模型跟踪(除其他外)每个资源的签出和签入时间。 (一个资源可以有许多事件)基本上,如果资源的 ID 列在事件行上,那么有人将其签出一段时间。我需要显示该资源在网站用户查看的日期/时间是否可供签出或已经签出。

模型(显示关系)

资源.php

class Resource extends Model

    /**
     * Define Relationships to other models.
     *
     */
    // The Event Model
    public function events()
    
        return $this->hasMany('App\Event');
    


事件.php

class Event extends Model

    /**
     * Define Relationships to other models.
     *
     */
    // To the User Model
    public function user()
    
        return $this->belongsTo('App\User');
    

    // To the Resource Model
    public function resource()
    
        return $this->belongsTo('App\Resource');
    

在控制器中,我拉取所有适用于日期的事件,如下所示:

$formStartOfDay = Carbon::parse($formDate)->startOfDay();
$formEndOfDay = Carbon::parse($formDate)->endOfDay();

$caseOne = Event::whereDate('outTime', '>=', $formStartOfDay)
    ->whereDate('inTime', '<=', $formEndOfDay);

$caseTwo = Event::whereDate('outTime', '=', $formStartOfDay)
    ->whereDate('inTime', '>', $formEndOfDay);

$caseThree = Event::whereDate('outTime', '<', $formStartOfDay)
    ->whereDate('inTime', '=', $formEndOfDay);

$caseFour = Event::whereDate('outTime', '<', $formStartOfDay)
    ->whereDate('inTime', '>', $formEndOfDay);

$events = $caseOne->union($caseTwo)
    ->union($caseThree)
    ->union($caseFour)
    ->orderBy('outTime')
    ->get();

同时,我拉取所有我需要的资源:

$resources = Resource::whereIn('type', ['SUV', 'Truck', 'Car'])->get()->sortby('name');

将它传递给视图后,我尝试创建我想要的表。列出每个资源(无论是否已签出)的表格。那些不在的资源将显示“已签出”并列出拥有该资源的人。一个资源可能一天被检出一次以上,如果它被检出,则需要显示所有实例。

    <thead class="bg-primary text-white">
        <tr class="text-center">
            <th scope="col">Vehicle</th>
            <th scope="col">Description</th>
            <th scope="col">Status</th>
            <th scope="col">Checked Out To</th>
        </tr>
    </thead>
    <tbody>
        @foreach ($resources as $resource)
        @foreach ($events as $resource_event)
        @if ($resource_event['resource_id'] == $resource->id)
        <tr class="table-default" id=" $resource->id ">
            @if ($loop->first)
            <td rowspan=" count($events->where('resource_id', $resource->id)) ">
                 $resource->name 
            </td>
            <td> $resource->description </td>
            @endif
            @if ($resource_event->inOrOut($resource_event->outTime,
            $resource_event->inTime) === "Out" )
            <td class="text-center">Checked Out</td>
            <td class="text-center"> $resource_event->user->name </td>
            @else
            <td class="text-center">Available</td>
            <td>&nbsp;</td>
            @endif
        </tr>
        @else
        <tr class="table-default" id=" $resource->id ">
            <td> $resource->name </td>
            <td> $resource->description </td>
            <td class="text-center">Available</td>
            <td>&nbsp;</td>
        </tr>
        @endif
        @endforeach
        @endforeach
    </tbody>
</table>

使用上面的刀片代码,我得到了每个资源的多个实例(每个 $event 一个),并且行跨度在它应该工作时只是吓坏了。我尝试了各种各样的东西,但没有一个能给我我想要的东西。我在这里,在谷歌,Laracasts 上搜索过,没有任何情况与我的完全一样,也没有帮助我解决问题。希望这里有人可以帮助我。

我对 ROWSPAN 的意图是使表(正常工作时)如下所示:

========================================================
| Vehicle | Description | Status      | Checked Out To |
|------------------------------------------------------|
| Truck 1 | 4x4 Truck   | Available   |                |
|------------------------------------------------------|
| Truck 2 | 4x4 Truck   | Checked Out | Person 1       |
|         |             |------------------------------|
|         |             | Checked Out | Person 2       |
|------------------------------------------------------|
| Truck 3 | 2WD Truck   | Available   |                |
========================================================

【问题讨论】:

我没有看到这里使用了 laravel 关系功能的迹象。 另外,你为什么打算使用行跨度?不是您想将每个事件都添加到它自己的行中吗?使用行跨度听起来并不那么有效和合法。 @AhmadKarimi,你说得对,这种关系不适用于这张桌子,因为我想不出一种可以以有用的方式使用它的方法。 (该关系正在应用程序的其他部分使用。)如果有一种方法可以在这里使用,我很想听听。 【参考方案1】:

解决方案

虽然可能有更好的方法来做到这一点,但我想出的解决方案是:

首先,使用带有约束的 Eager Loading 从控制器中的数据库中获取我需要的组合数据。

// Establish the earliest and latest times of the form's date
$formStartOfDay = Carbon::parse($formDate)->startOfDay();
$formEndOfDay = Carbon::parse($formDate)->endOfDay();

// Gather needed data from the database.
// Get the data via Eager Loading
$resources = Resource::whereIn('type', ['SUV', 'Truck', 'Car'])->orderBy('name') 
    ->with(['events' => function ($query) use ($formStartOfDay, $formEndOfDay) 
        $query->whereDate('outTime', '>=', $formStartOfDay)->whereDate('inTime', '<=', $formEndOfDay)
        ->orWhereDate('outTime', '=', $formStartOfDay)->whereDate('inTime', '>', $formEndOfDay)
        ->orWhereDate('outTime', '<', $formStartOfDay)->whereDate('inTime', '=', $formEndOfDay)
        ->orWhereDate('outTime', '<', $formStartOfDay)->whereDate('inTime', '>', $formEndOfDay)
        ->orderBy('outTime');
    ])->get();

接下来我修改了 Blade 模板以使用新的集合:

<table class="table table-bordered table-sm">
    <thead class="bg-primary text-white">
        <tr class="text-center">
            <th scope="col">Vehicle</th>
            <th scope="col">Description</th>
            <th scope="col">Status</th>
            <th scope="col">Checked Out To</th>
            <th scope="col" style="width: 9%">Returning</th>
        </tr>
    </thead>
    <tbody>
        @foreach ($resources as $resource)
        @if ($resource->events->contains('resource_id', $resource->id))
        @foreach ($resource->events as $resource_event)
        <tr class="table-default" id=" $resource->id ">
            <td> $resource->name </td>
            <td> $resource->description </td>
            @if ($resource_event->inOrOut($resource_event->outTime, $resource_event->inTime) === "Out" )
            <td class="text-center">Checked Out</td>
            <td class="text-center">
                 $resource_event->user->name <br />
            </td>
            <td class="text-center">
                 \Carbon\Carbon::parse($resource_event->inTime)->format('m/d/Y g:i A') 
            </td>
            @else
            <td class="text-center">Available</td>
            <td>&nbsp;</td>
            <td>&nbsp;</td>
            @endif
        </tr>
        @endforeach
        @else
        <tr class="table-default" id=" $resource->id ">
            <td> $resource->name </td>
            <td> $resource->description </td>
            <td class="text-center">Available</td>
            <td>&nbsp;</td>
            <td>&nbsp;</td>
        </tr>
        @endif
        @endforeach
    </tbody>
</table>

解决方案的关键是使用适当的约束进行预加载。这样我就收到了所有的“资源”,但只有我需要的“事件”。这让我能够极大地简化 Blade 代码,并让我远离非常复杂的循环和 if-else 块。

我希望这对将来的其他人有所帮助。

【讨论】:

以上是关于Laravel 刀片表创建嵌套的 Foreach 循环的主要内容,如果未能解决你的问题,请参考以下文章

编辑表单刀片 Laravel 中的嵌套循环

刀片模板中的多对多关系中的 Laravel 嵌套查询

Laravel:刀片 foreach 循环引导列

Laravel 刀片模板,URL::to 中的 foreach 变量?

如何在刀片模板的 foreach 循环中访问 laravel 集合?

如何在 Laravel 刀片类别下获取子类别