2017-01-26 73 views
1

我有以下两个集合:Laravel 5.1 - 与过滤类别不工作

$credits = collect([ 
    ['quantity' => 3, 'product_id' => 1], 
    ['quantity' => 2, 'product_id' => 5] 
]); 

$basketItems = collect([ 
    ['id' => 1, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 2, 'basket_id' => 4, 'product_id' => 2], 
    ['id' => 3, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 4, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 5, 'basket_id' => 4, 'product_id' => 1] 
]); 

$credits集合告诉我们用户有3个可用信用用于product_id 1.

现在我想创建两个新的集合。如果用户拥有可获得积分的篮子物品 - 这可以由product_id确定,那么我想将这些物品添加到名为$basketItemsUseCredits的新集合中。

如果购物篮商品没有可用的商品类型积分,我想将这些商品添加到另一个名为$basketItemsPay的收藏中。

因此,在上面的示例中,我应该以$basketItemsUseCredits结尾的id为1,3和4的basketitems。$basketItemsPay应该以id为2和5的basketsitems结束。以下操作不起作用。

$basketItemsUseCredits = $basketItems->map(function ($basketItem) use ($credits) { 

     $qty = $credits->where('product_id', $basketItem->product_id)->get('quantity', 0); 

     if ($qty > 0) { 
      // update quantity 
      $credits->transform(function ($credit) use ($basketItem) { 
        if ($credit->product_id == $basketItem->product_id) { 
         $credit->quantity = $credit->quantity - 1; 
         return $credit; 
        } 
        else 
         return $credit 
      });    

      return $basketItem; 
     } 
    }) 


    $basketItemsPay = $basketItems->map(function ($basketItem) use ($basketItemsUseCredits) { 

     if (!$basketItemsUseCredits->contains('id', $basketItem->id)) 
       return $basketItem; 
    }); 

    if(!$basketItemsPay->isEmpty()) { 
     // do some extra stuff 
    } 

下面一行总是返回0:

$qty = $credits->where('product_id', $basketItem->product_id)->get('quantity', 0); 

另外还有一件事我注意到。如果$basketItemsPay为空,例如如果我我dd($basketItemsPay)得到如下:

Collection {#316 ▼ 
    #items: array:1 [▼ 
     0 => null 
    ] 
} 

那么,为什么下面始终为true,在上述情形?

if(!$basketItemsPay->isEmpty()) { 
     // do some extra stuff 
} 

任何帮助表示赞赏。

* UPDATE *

管理通过执行以下操作来解决 - 除非有任何人知道一个更好的解决方案:

$qty = $credits->where('product_id', $basketItem->product_id)->first()['quantity']; 

和链接的拒绝方法如下摆脱空值 - 有没有人知道更优雅的解决方案?

$basketItemsPay = $basketItems->map(function ($basketItem) use ($basketItemsUseCredits) { 

     if (!$basketItemsUseCredits->contains('id', $basketItem->id)) 
      return $basketItem; 

})->reject(function ($basketItem) { 
     return empty($basketItem); 
}); 
+0

你有一个很好的理由不实现每个嵌套数组中的'$ basketItems'内'quantity'关键像你用'$ credits'做的阵列?我认为这会让事情变得更容易。你能否更详细地告诉我这是行不通的?像这个代码的输出是什么,它与期望的结果有什么不同? –

+0

@Jeffrey在$ basketItems数组中有一个'quantity'键,我没有显示它来保持简单。下面这行总是返回0:'$ credits->其中('product_id',$ basketItem-> product_id) - > get('quantity',0);' – adam78

+0

好吧,但要清楚,每个单独的'$ basketItem '没有'数量'键,这就是为什么同一个产品ID可能会多次出现的原因。我会试着想出一个答案,可能需要我一段时间:) –

回答

0

花了一段时间,以及弄清楚这一点。以下是我将如何处理这个问题。这是一个稍微更线性的方法,并且比收集方法中的闭包更容易理解。它比你的例子还要多一点代码,但这是个人喜好的问题。我尽量在代码注释中尽可能描述。

在刁难自己,更容易最重要的部分在于,在起步改造$credits集合的结构:

// First, let's simplify the $credits structure for easy checking. NOTE: this collection WILL mutate by the code below. 
// If the original version is to be preserved for whatever reason, now's the time to duplicate it :) 
$credits = collect([ 
    ['quantity' => 3, 'product_id' => 1], 
    ['quantity' => 2, 'product_id' => 5] 
]) 
    ->pluck('quantity', 'product_id') 

/* Results in a collection where keys are product_id, and values are quantity: 

    Illuminate\Support\Collection { 
     all: [ 
      1 => 3, 
      5 => 2 
     ] 
    } 

*/ 

$basketItems = collect([ 
    ['id' => 1, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 2, 'basket_id' => 4, 'product_id' => 2], 
    ['id' => 3, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 4, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 5, 'basket_id' => 4, 'product_id' => 1] 
]); 


$basketItemsUseCredits = new \Illuminate\Support\Collection; 

// First, we will filter the $basketItems collection by the condition that the product_id for each item 
// does NOT occur as key inside $credits. NOTE: filter() returns a new collection. The original $basketItems 
// does not mutate because of this call. 
$basketItemsPay = $basketItems->reject(function ($basketItem) use ($credits) { 

    // If credits has a key corresponding the product_id of the current $basketItem, REJECT the $basketItem 
    return $credits->has($basketItem['product_id']); 
}); 



// Now, we will create an intermediate collection of basket items that need to be compared against the quantity 
// of credits available for products of the ID's present in this collection: 
$basketItemsCountCredits = $basketItems->filter(function ($basketItem) use ($credits) { 

    // If credits has a key corresponding the product_id of the current $basketItem, KEEP the $basketItem 
    return $credits->has($basketItem['product_id']); 
}); 

// Lastly we will iterate the $basketItemsCountCredits collection, and determine whether credits are still available 
// for each item. If so, push the item to $basketItemsUseCredits AND decrement the amount of available credits for 
// the item's ID. otherwise just push the item to $basketItemsPay. 
foreach ($basketItemsCountCredits as $item) { 

    $productId = $item['product_id']; 
    $remainingCredits = $credits->get($productId); 

    // If more than 0 credits are available for products with ID of $item['product_id'] 
    if ($remainingCredits > 0) { 

     // .. push the $item to $basketItemsUseCredits, 
     $basketItemsUseCredits->push($item); 

     // .. and decrement the amount of available credits. 
     // Collection->put() overwrites the key => value pair in the collection if the key already exists. 
     $credits->put($productId, $remainingCredits - 1); 
    } else { 

     // The amount of available credits might have become 0 in previous iterations of this for-loop 
     $basketItemsPay->push($item); 
    } 
} 

上照亮集合There are a lot of pretty powerful methods available。当以正确的方式使用它们时,它们可以允许一些非常简洁的代码!

但是,这些相同的方法也可以设置回关于代码可读性和 - 在不必要的地方使用时的复杂性。当某个回调函数的定义在其他地方完成时,您将传递回调函数的方法在某些情况下非常有用,并且它依赖于该上下文来完成其工作。从你的例子来看,这似乎并不是这种情况。

而不是连续使用filterreject方法,就像我在上面的例子中所做的那样,您可以使用良好的旧的foreach循环同时执行这两个操作。然而,这需要预先将$basketItemsCountCredits$basketItemsPay变量初始化为新的集合。如果我们还剥夺了评论,它实际上不是那么多的代码,仍然完全可读:)

$credits = collect([ 
    ['quantity' => 3, 'product_id' => 1], 
    ['quantity' => 2, 'product_id' => 5] 
])->pluck('quantity', 'product_id') 

$basketItems = collect([ 
    ['id' => 1, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 2, 'basket_id' => 4, 'product_id' => 2], 
    ['id' => 3, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 4, 'basket_id' => 4, 'product_id' => 1], 
    ['id' => 5, 'basket_id' => 4, 'product_id' => 1] 
]); 


$basketItemsCountCredits = new \Illuminate\Support\Collection; 
$basketItemsUseCredits = new \Illuminate\Support\Collection; 
$basketItemsPay   = new \Illuminate\Support\Collection; 

// A foreach loop is perfectly sane here 
foreach ($basketItems as $item) { 
    if ($credits->has($item['product_id'])) { 
     $basketItemsCountCredits->push($item); 
    } else { 
     $basketItemsPay->push($item); 
    } 
}); 

foreach ($basketItemsCountCredits as $item) { 

    $productId = $item['product_id']; 
    $remainingCredits = $credits->get($productId); 

    if ($remainingCredits > 0) { 
     $basketItemsUseCredits->push($item); 
     $credits->put($productId, $remainingCredits - 1); 
    } else { 
     $basketItemsPay->push($item); 
    } 
}