英文:
Laravel collection / blade - displaying unique rows
问题
我希望能够在我的网站仪表板上为每个用户显示未来一周的餐点清单。
这个例子中,每天有3顿饭。
我的数据库结构如下:
$table->id();
$table->foreignId('user_id')->constrained('users');
$table->enum('day', ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']);
$table->foreignId('meal_id')->nullable()->constrained('food_meals');
$table->enum('type', ['breakfast', 'lunch', 'dinner']);
$table->string('slug')->unique();
$table->timestamps();
我的User
模型中有一个简单的关系设置:
public function userMeals()
{
return $this->hasMany(Meal::class);
}
我从数据库中获取的数据如下:
$meals = auth()->user()->userMeals()->get();
当在Blade中循环遍历数据时,每天都会显示三次。但我希望的是每天只显示一次,而在数据库中的数据如下:
[![Database][3]][3]
所以,基本上我的问题是,我能否使用这种设置在我的Blade文件中显示数据,就像第二张图片中那样,以便我可以在控制器中编辑数据?还是这种设计有问题,应该采用不同的方法?
PS. 例如,如果我执行以下操作,它可以工作,但我永远无法显示早餐、午餐和晚餐的初始数据,因为每天将会丢失两条记录:
auth()->user()->userMeals()->get()->unique('day')
英文:
Wasn't quite sure how to formulate the question, so I hope it represents my issue.
What I want to achieve is, displaying on my website's dashboard, for each user a list of meals for the upcoming week.
And in this example it will be 3 meals a day.
My database for this looks like this:
$table->id();
$table->foreignId('user_id')->constrained('users');
$table->enum('day', ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']);
$table->foreignId('meal_id')->nullable()->constrained('food_meals');
$table->enum('type', ['breakfast', 'lunch', 'dinner']);
$table->string('slug')->unique();
$table->timestamps();
I have a simple relation setup in my User model
:
public function userMeals()
{
return $this->hasMany(Meal::class);
}
And the data returned to my dashboard is this:
$meals = auth()->user()->userMeals()->get();
When looping over the data in my blade, I of course get each day displayed three times:
But what I want of course is this:
And in my database I store the data like this:
So 3 entries per day, based on the type of meal.
So basically my question is this. Can I have the data displayed in my blade file like I have on the second picture with this setup, so I can edit the data in my controller? Or is this bad design and should I go about it differently?
PS. When I do this for example, it works, but I can never display the initial data for the breakfast, lunch and dinner, because two entries for each day will be lost:
auth()->user()->userMeals()->get()->unique('day')
答案1
得分: 2
你可以在你的 User.php
模型上定义一个属性方法:
public function getMealsGroupedByDayAndTypeAttribute()
{
$grouped = [];
$days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
$types = ['breakfast', 'lunch', 'dinner'];
foreach ($days as $day) {
foreach ($types as $type) {
$grouped[$day][$type] = $this->userMeals->where('day', $day)->where('type', $type)->first();
}
}
return $grouped;
}
auth()->user()->meals_grouped_by_day_and_type
的示例输出如下:
[
'monday' => [
'breakfast' => [
'id' => 1,
'user_id' => 1,
'day' => 'monday',
'type' => 'breakfast',
'meal_id' => 1,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'lunch' => [
'id' => 2,
'user_id' => 1,
'day' => 'monday',
'type' => 'lunch',
'meal_id' => 2,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'dinner' => [
'id' => 3,
'user_id' => 1,
'day' => 'monday',
'type' => 'dinner',
'meal_id' => 3,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
],
'tuesday' => [
'breakfast' => [
'id' => 4,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'breakfast',
'meal_id' => 4,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'lunch' => [
'id' => 5,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'lunch',
'meal_id' => 5,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'dinner' => [
'id' => 6,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'dinner',
'meal_id' => 6,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
],
// ...
]
因此,你可以遍历第一层的天和类型。
或者你可以直接检查一天的餐类型是否存在,并用它来填充选择框:
if(isset(auth()->user()->meals_grouped_by_day_and_type['monday']['lunch'])) { //... }
在使用之前,请仔细检查代码。此代码始终仅筛选每天每种类型的第一条记录。例如,如果你有两条记录 day == 'monday'
和 type == 'lunch'
,它只会筛选出第一条记录。
此代码还使用 $this->userMeals->where(...
,而不是 $this->userMeals()->where(...
。这意味着模型只查询数据库一次,并将其加载为一个集合到 $this->userMeals
,因此 where()
和 first()
方法在集合上工作,而不是在查询生成器上。
如果你使用括号,如 $this->userMeals()->where(...
,它将为数据库创建一个新的查询,并且不会那么高效。
英文:
You can define an attribute method on your User.php
model:
public function getMealsGroupedByDayAndTypeAttribute()
{
$grouped = [];
$days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
$types = ['breakfast', 'lunch', 'dinner'];
foreach ($days as $day) {
foreach ($types as $type) {
$grouped[$day][$type] = $this->userMeals->where('day', $day)->where('type', $type)->first();
}
}
return $grouped;
}
An example output of auth()->user()->meals_grouped_by_day_and_type
will be like this:
[
'monday' => [
'breakfast' => [
'id' => 1,
'user_id' => 1,
'day' => 'monday',
'type' => 'breakfast',
'meal_id' => 1,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'lunch' => [
'id' => 2,
'user_id' => 1,
'day' => 'monday',
'type' => 'lunch',
'meal_id' => 2,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'dinner' => [
'id' => 3,
'user_id' => 1,
'day' => 'monday',
'type' => 'dinner',
'meal_id' => 3,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
],
'tuesday' => [
'breakfast' => [
'id' => 4,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'breakfast',
'meal_id' => 4,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'lunch' => [
'id' => 5,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'lunch',
'meal_id' => 5,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
'dinner' => [
'id' => 6,
'user_id' => 1,
'day' => 'tuesday',
'type' => 'dinner',
'meal_id' => 6,
'created_at' => '2021-05-04T20:00:00.000000Z',
'updated_at' => '2021-05-04T20:00:00.000000Z',
],
],
// ...
]
So you can loop through first level for days and types in that.
Or you can directly check if a meal type of a day exists and use it to fill select boxes
if(isset(auth()->user()->meals_grouped_by_day_and_type['monday']['lunch'])) { //... }
Please carefully check the code before use on production. This code always filters only first record of each days each type. For example if you have 2 records for day=='monday'
and type='lunch'
it will filter just the first one.
Also this codes uses $this->userMeals->where(...
, not $this->userMeals()->where(...
. It means model only queries the database for 1 time and loads it as a collection on $this->userMeals
so where()
and first()
methods works on a Collection
not on a QueryBuilder
If you use it with parentheses like $this->userMeals()->where(...
it will create a new query for the database and won't be so efficient.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论