From 35154dc7a32d892da5f0238d2ae9d9e92af2f6c6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 29 Dec 2015 18:55:30 +0100 Subject: [PATCH] Another chart optimised --- .../Controllers/Chart/CategoryController.php | 130 +++++------------- .../Category/CategoryRepository.php | 111 ++++++++++++++- .../Category/CategoryRepositoryInterface.php | 26 ++++ 3 files changed, 172 insertions(+), 95 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index c71e2ef579..7d6a97b24a 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -304,8 +304,7 @@ class CategoryController extends Controller */ public function earnedInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $original = clone $start; - $cache = new CacheProperties; // chart properties for cache: + $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($reportType); @@ -315,78 +314,42 @@ class CategoryController extends Controller 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( + $set = $repository->earnedForAccountsPerMonth($accounts, $start, $end); + $categories = $set->unique('id')->sortBy( function (Category $category) { return $category->name; } ); + $entries = new Collection; - // 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); + while ($start < $end) { // filter the set: + $row = [clone $start]; + // get possibly relevant entries from the big $set + $currentSet = $set->filter( + function (Category $category) use ($start) { + return $category->dateFormatted == $start->format("Y-m"); } ); - // create a row used later on. - $row = [clone $currentStart]; - - // loop all categories: + // check for each category if its in the current set. /** @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; + // if its in there, use the value. + $entry = $currentSet->filter( + function (Category $cat) use ($category) { + return ($cat->id == $category->id); } - ); - // save amount + )->first(); if (!is_null($entry)) { - $row[] = $entry->earned; + $row[] = round($entry->earned, 2); } else { $row[] = 0; } } + $entries->push($row); $start->addMonth(); } - $data = $this->generator->earnedInPeriod($categories, $entries); $cache->store($data); @@ -408,8 +371,7 @@ class CategoryController extends Controller */ public function spentInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $original = clone $start; - $cache = new CacheProperties; // chart properties for cache: + $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($reportType); @@ -419,66 +381,46 @@ class CategoryController extends Controller 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(); - $set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd); - $categories = $categories->merge($set); - $sets->push([$currentStart, $set]); - $start->addMonth(); - } - $categories = $categories->unique('id'); - $categories = $categories->sortBy( + $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); + $categories = $set->unique('id')->sortBy( function (Category $category) { return $category->name; } ); + $entries = new Collection; - $start = clone $original; - while ($start < $end) { - $currentEnd = clone $start; - $currentStart = clone $start; - $currentStart->startOfMonth(); - $currentEnd->endOfMonth(); - $currentSet = $sets->first( - function ($key, $value) use ($currentStart) { - // set for this date. - return ($value[0] == $currentStart); + while ($start < $end) { // filter the set: + $row = [clone $start]; + // get possibly relevant entries from the big $set + $currentSet = $set->filter( + function (Category $category) use ($start) { + return $category->dateFormatted == $start->format("Y-m"); } ); - $row = [clone $currentStart]; - + // check for each category if its in the current set. /** @var Category $category */ foreach ($categories as $category) { - /** @var Category $entry */ - $entry = $currentSet[1]->first( - function ($key, $value) use ($category) { - return $value->id == $category->id; + // if its in there, use the value. + $entry = $currentSet->filter( + function (Category $cat) use ($category) { + return ($cat->id == $category->id); } - ); + )->first(); if (!is_null($entry)) { - $row[] = $entry->spent; + $row[] = round(($entry->spent * -1), 2); } else { $row[] = 0; } } + $entries->push($row); $start->addMonth(); } - $data = $this->generator->spentInPeriod($categories, $entries); $cache->store($data); return $data; - } } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 3af5b4bff1..e97bcb2dff 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -531,6 +531,61 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito } + /** + * Returns a collection of Categories appended with the amount of money that has been earned + * in these categories, based on the $accounts involved, in period X, grouped per month. + * The amount earned in category X in period X is saved in field "earned". + * + * @param $accounts + * @param $start + * @param $end + * + * @return Collection + */ + public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end) + { + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + + + $collection = Auth::user()->categories() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') + ->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions AS t_src', function (JoinClause $join) { + $join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_dest', function (JoinClause $join) { + $join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0); + } + ) + ->whereIn('t_dest.account_id', $accountIds)// to these accounts (earned) + ->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts + ->whereIn( + 'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE] + ) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->groupBy('categories.id') + ->groupBy('dateFormatted') + ->get( + [ + 'categories.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'), + DB::Raw('SUM(`t_dest`.`amount`) AS `earned`') + ] + ); + + return $collection; + + + } + /** * Returns a collection of Categories appended with the amount of money that has been earned * in these categories, based on the $accounts involved, in period X. @@ -568,7 +623,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito ->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts ->whereIn( 'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE] - )// earned from these things. + ) ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('categories.id') @@ -624,4 +679,58 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito return $collection; } + + /** + * Returns a collection of Categories appended with the amount of money that has been spent + * in these categories, based on the $accounts involved, in period X, grouped per month. + * The amount earned in category X in period X is saved in field "spent". + * + * @param $accounts + * @param $start + * @param $end + * + * @return Collection + */ + public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end) + { + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + + + $collection = Auth::user()->categories() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') + ->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions AS t_src', function (JoinClause $join) { + $join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_dest', function (JoinClause $join) { + $join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0); + } + ) + ->whereIn('t_src.account_id', $accountIds)// from these accounts (spent) + ->whereNotIn('t_dest.account_id', $accountIds)//-- but not from these accounts (spent internally) + ->whereIn( + 'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE] + )// spent on these things. + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->groupBy('categories.id') + ->groupBy('dateFormatted') + ->get( + [ + 'categories.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'), + DB::Raw('SUM(`t_src`.`amount`) AS `spent`') + ] + ); + + return $collection; + } + } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 34553e7ea3..a3d82b8db4 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -63,6 +63,32 @@ interface CategoryRepositoryInterface */ public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end); + /** + * Returns a collection of Categories appended with the amount of money that has been spent + * in these categories, based on the $accounts involved, in period X, grouped per month. + * The amount earned in category X in period X is saved in field "spent". + * + * @param $accounts + * @param $start + * @param $end + * + * @return Collection + */ + public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end); + + /** + * Returns a collection of Categories appended with the amount of money that has been earned + * in these categories, based on the $accounts involved, in period X, grouped per month. + * The amount earned in category X in period X is saved in field "earned". + * + * @param $accounts + * @param $start + * @param $end + * + * @return Collection + */ + public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end); + /** * @return Collection */