/**
  * Returns a chart of what has been earned in this period in each category
  * grouped by month.
  *
  * @param CategoryRepositoryInterface $repository
  * @param                             $report_type
  * @param Carbon                      $start
  * @param Carbon                      $end
  * @param Collection                  $accounts
  *
  * @return \Illuminate\Http\JsonResponse
  */
 public function earnedInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
 {
     $original = clone $start;
     $cache = new CacheProperties();
     // chart properties for cache:
     $cache->addProperty($start);
     $cache->addProperty($end);
     $cache->addProperty($report_type);
     $cache->addProperty($accounts);
     $cache->addProperty('category');
     $cache->addProperty('earned-in-period');
     if ($cache->has()) {
         return Response::json($cache->get());
         // @codeCoverageIgnore
     }
     $categories = new Collection();
     $sets = new Collection();
     $entries = new Collection();
     // run a very special query each month:
     $start = clone $original;
     while ($start < $end) {
         $currentEnd = clone $start;
         $currentStart = clone $start;
         $currentStart->startOfMonth();
         $currentEnd->endOfMonth();
         // get a list of categories, and what the user has earned for that category
         // (if the user has earned anything)
         $set = $repository->earnedForAccounts($accounts, $currentStart, $currentEnd);
         $categories = $categories->merge($set);
         // save the set combined with the data that is in it:
         // for example:
         // december 2015, salary:1000, bonus:200
         $sets->push([$currentStart, $set]);
         $start->addMonth();
     }
     // filter categories into a single bunch. Useful later on.
     // $categories contains all the categories the user has earned money
     // in in this period.
     $categories = $categories->unique('id');
     $categories = $categories->sortBy(function (Category $category) {
         return $category->name;
     });
     // start looping the time again, this time processing the
     // data for each month.
     $start = clone $original;
     while ($start < $end) {
         $currentEnd = clone $start;
         $currentStart = clone $start;
         $currentStart->startOfMonth();
         $currentEnd->endOfMonth();
         // in $sets we have saved all the sets of data for each month
         // so now we need to retrieve the corrent one.
         // match is on date of course.
         $currentSet = $sets->first(function ($key, $value) use($currentStart) {
             // set for this date.
             return $value[0] == $currentStart;
         });
         // create a row used later on.
         $row = [clone $currentStart];
         // loop all categories:
         /** @var Category $category */
         foreach ($categories as $category) {
             // if entry is not null, we've earned money in this period for this category.
             $entry = $currentSet[1]->first(function ($key, $value) use($category) {
                 return $value->id == $category->id;
             });
             // save amount
             if (!is_null($entry)) {
                 $row[] = $entry->earned;
             } else {
                 $row[] = 0;
             }
         }
         $entries->push($row);
         $start->addMonth();
     }
     $data = $this->generator->earnedInPeriod($categories, $entries);
     $cache->store($data);
     return $data;
 }