Laravel how to merge 2 collections - laravel

I have 2 collections as below:
$unpaid = WhmcsClientsInvoice::join('quotations', 'quotations.id', 'whmcs_clients_invoices.quotation_id')
->select('whmcs_clients_invoices.invoice_id AS id', 'whmcs_clients_invoices.invoice_date AS date', 'quotations.total AS amount', 'whmcs_clients_invoices.payment_due_date AS due_date')
->where('whmcs_clients_invoices.whmcs_client_id', $client_id)
->where('whmcs_clients_invoices.status', '!=', 'Cancel')
->whereBetween('whmcs_clients_invoices.invoice_date', [$start, $end])
->orderBy('date', 'asc')
->get();
foreach($unpaid as $un) { $un['type'] = "invoice"; }
$paid = InvoiceHistory::join('whmcs_clients_invoices', 'whmcs_clients_invoices.id', 'invoice_history.whmcs_clients_invoices_id')
->select('invoice_history.id', 'invoice_history.date_paid AS date', 'invoice_history.amount_paid AS amount', 'invoice_history.created_at AS due_date')
->where('whmcs_clients_invoices.whmcs_client_id', $client_id)
->where('whmcs_clients_invoices.status', '!=', 'Cancel')
->whereBetween('invoice_history.date_paid', [$start, $end])
->orderBy('date', 'asc')
->get();
foreach ($paid as $paid) { $paid['type'] = 'payment'; }
$trans = $unpaid->merge($paid);
dd($trans);
But when I try to merge these 2 collections, an error will occur.
I tried $transaction = $unpaid->union($paid)->sortBy('date'); but to no avail. Vardump of the merged collection shows:
error_log the $transaction would show something like this:
{"date":"2021-01-26
00:00:00","id":12,"amount":111.3,"due_date":"2021-01-26
09:00:05","type":"payment","0":{"id":1,"date":"2021-01-07","amount":222.6,"due_date":"2021-01-14","type":"invoice"},"1":{"id":3,"date":"2021-01-09","amount":6572,"due_date":"2021-01-16","type":"invoice"},"2":{"id":4,"date":"2021-01-12","amount":148.4,"due_date":"2021-01-19","type":"invoice"},"3":{"id":5,"date":"2021-01-12","amount":144.16,"due_date":"2021-01-19","type":"invoice"},"4":{"id":6,"date":"2021-01-16","amount":24.38,"due_date":"2021-01-23","type":"invoice"},"5":{"id":2,"date":"2021-01-29","amount":222.6,"due_date":"2021-01-14","type":"invoice"},"6":{"id":24,"date":"2021-02-23","amount":190.8,"due_date":"2021-03-02","type":"invoice"}}
Apparently the union merge the 2 collections, but couldn't assign key to the first item...
How do I merge 2 collections without missing any records in both collections? The expected output should be from this:
Collection {#595 ▼
#items: array:3 [▼
0 => WhmcsClientsInvoice {
"id" = 37
"date" => "2021-02-07 00:00:00"
"amount" => 50.0
"due_date" => "2021-02-07 14:19:20"
"type" => "payment"
}
2 => InvoiceHistory {
"id" = 37
"date" => "2021-02-07 00:00:00"
"amount" => 50.0
"due_date" => "2021-02-07 14:19:20"
"type" => "payment"
}
1 => WhmcsClientsInvoice {
"id" = 37
"date" => "2021-02-07 00:00:00"
"amount" => 50.0
"due_date" => "2021-02-07 14:19:20"
"type" => "payment"
}
]
}

This happens sometimes,
Try this
$trans = $unpaid->concat($paid);

After a few days of painstaking research, I found a way that works for me. This is my updated code:
$transaction = collect(new WhmcsClientsInvoice);
$unpaid = WhmcsClientsInvoice::join('quotations', 'quotations.id', 'whmcs_clients_invoices.quotation_id')
->select('whmcs_clients_invoices.invoice_id', 'whmcs_clients_invoices.invoice_num', 'whmcs_clients_invoices.invoice_date AS date', 'quotations.total AS amount', 'whmcs_clients_invoices.payment_due_date AS due_date')
->where('whmcs_clients_invoices.whmcs_client_id', $client_id)
->where('whmcs_clients_invoices.status', '!=', 'Cancel')
->whereBetween('whmcs_clients_invoices.invoice_date', [$start, $end])
->orderBy('date', 'asc')
->get();
foreach($unpaid as $un) {
$un['type'] = "invoice";
$transaction->push($un);
}
$paid = InvoiceHistory::join('whmcs_clients_invoices', 'whmcs_clients_invoices.id', 'invoice_history.whmcs_clients_invoices_id')
->select('invoice_history.date_paid AS date', 'invoice_history.remark', 'invoice_history.amount_paid AS amount', 'invoice_history.created_at AS due_date')
->where('whmcs_clients_invoices.whmcs_client_id', $client_id)
->where('whmcs_clients_invoices.status', '!=', 'Cancel')
->whereBetween('invoice_history.date_paid', [$start, $end])
->orderBy('date', 'asc')
->get();
foreach ($paid as $pa) {
$pa['type'] = "payment";
$transaction->push($pa);
}
$transaction = $transaction->sortBy('date');
First, I make an empty collection based on WhmcsClientInvoices called Transaction. Basically, my idea is to create 2 separate collections, then merge them into Transaction. So, I made 2 eloquents to get 2 different collections. Then, during the foreach, I added a new item with key call type, to differentiate whether each record is invoice or payment. Then, I take each individual record in collection and push it to Transaction. I repeat the process with $paid collection. Lastly, I sort the collection by date.

Related

How to use groupBy Month and Sum in laravel MongoDB

Query to get monthly orders (total orders per month(count) or total sales per month(sum)):
Tried this query, but it is not working. Also tried with other results from StackOverflow, but I didn't understand how it is done with MongoDB query. This is the link to that question : select-sum-column-and-group-by-with-mongodb-and-laravel
$monthly_orders = Order::select(
DB::raw('sum(total_amount) as sum'),
DB::raw('YEAR(created_at) year, MONTH(created_at) month'),
)
->groupBy('month')
->get();
When I try to get total amount by using group by customer ID , it is returning sum as null
$monthly_orders = Order::selectRaw('sum(total_amount) as sum, customer_id')
->groupBy('customer_id')
->pluck('sum', 'customer_id');
Result :
Illuminate\Support\Collection {#2123 ▼
#items: array:4 [▼
"6098e5ff5977a25ee96a2a42" => null
"60dbf87f7d8ffb7cdb2233d2" => null
"605af409195d8e59e34893f2" => null
"60ddae4a66fb69678e45f056" => null
]
}
Try using raw and aggregate
$monthly_orders = Order::raw(function ($collection) {
return $collection->aggregate([
[
'$group' => [
"_id" => '$customer_id',
'customer_id' => ['$first' => '$customer_id'],
'sum' => ['$sum' => '$total_amount']
]
],
]);
});
you can use pluck
$monthly_orders->pluck('sum','customer_id')
Group by month
$monthly_orders = Order::raw(function ($collection) {
return $collection->aggregate([
[
'$group' => [
"_id" => ['$month'=>'$created_at'],
'customer_id' => ['$first' => '$customer_id'],
'sum' => ['$sum' => '$total_amount']
]
],
]);
});

How to sort product at laravel by relationship?

I filter and list the products listed with the code samples on my model page below with some data from the user.
I want to sort the listed products according to their prices. However, as it is seen in the minprice-maxprice sample code block, relation depends on several conditions.
From the period consisting of postFrom and postTo dates received by the user, if the daily is 0, it should be listed according to the old_daily price, if the daily is not 0, it should be listed according to the daily price.
How can I do that?
my model page
public $belongsTo = [
'price' => [
'ac\prices\models\Price',
'key' => 'id',
'otherKey' => 'pro_id',
],
]
public static $allowedSortingOptions = array (
'name desc' => 'Name - desc',
'name asc' => 'Name - asc',
'price desc' => 'Price - desc',
'price asc' => 'Price - asc',
);
public function scopeListFrontEnd($query, $options = []){
extract(array_merge([
'page' => 1,
'perPage' => 10,
'sort' => 'created_at desc',
'postFrom' => null,
'postTo' => null,
'minPrice' => null,
'maxPrice' => null,
], $options));
if(!is_array ($sort)){
$sort = [$sort];
}
foreach ($sort as $_sort){
if(in_array($_sort, array_keys(self::$allowedSortingOptions))){
$parts = explode(' ', $_sort);
if(count($parts) < 2){
array_push($parts, 'desc');
}
list($sortField, $sortDirection) = $parts;
$query->orderBy($sortField, $sortDirection);
}
}
if($minPrice != null) {
if(!is_array($minPrice)){
$minPrice = [$minPrice];
}
foreach ($minPrice as $mnPrice){
$query->whereHas('price', function($q) use ($mnPrice,$maxPrice,$postFrom,$postTo){
$q->where('daily', '==', '0')
->where(function( $query ) use ( $mnPrice, $maxPrice ) {
$query->where('old_daily', '>=', $mnPrice);
$query->where('old_daily', '<=', $maxPrice[0]);
});
$q->orWhere('daily', '!=', '0')
->where(function( $query ) use ( $mnPrice, $maxPrice ) {
$query->where('daily', '>=', $mnPrice);
$query->where('daily', '<=', $maxPrice[0]);
});
$q->when($postFrom == '0', function ($sq) {
$sq->where('id', '>', '0');
}, function ($ssq) use ($postFrom, $postTo) {
$ssq->where(function($q) use ($postFrom) {
$q->whereDate('start_date', '<=', $postFrom[0])
->whereDate('end_date', '>=', $postFrom[0]);
})->orWhere(function($q) use ($postTo) {
$q->whereDate('start_date', '<=', $postTo[0])
->whereDate('end_date', '>=', $postTo[0]);
});
});
});
}
}
$lastPage = $query->paginate($perPage, $page)->lastPage();
if($lastPage < $page){
$page = 1;
}
return $query->paginate($perPage, $page);
}
Without trying to decode exactly what you are trying to do here, I would be adding a sub-query select that pulls a sort_price field into the results that you can then orderBy.
$query->selectRaw('CASE WHEN daily = 0 THEN old_daily ELSE daily END as sort_price');
$query->orderByRaw('(SELECT sort_price)');
You can also do this directly in the sort condition as per MYSQL ORDER BY CASE Issue if you don't need this price in your result.
You can do this in the orderByRaw builder method.

laravel collection groupBy() / include empty results

For a chart API I would need to provide the count of registered users per day.
//fetch all created_at dates of users from the last week
$signUpsLastWeek = User::whereDate('created_at', '>=', now()->subDays(7))->select('created_at')->get();
//group them now by date now, using collection operations
dd($signUpsLastWeek->groupBy(function($item) {
return $item->created_at->format('m.d');
}));
//now manipulate the collection a bit, so we get the date with the amount of new registered users
$signUpsLastWeek->mapWithKeys(function ($userGroup, $key) {
return [$key => $userGroup->count()];
})
Returns:
Illuminate\Database\Eloquent\Collection {#774 ▼
#items: array:1 [▼
"01.19" => 4
]
}
This works fine, a question is left.
In the example code above there are 0 new signups on the other days, meaning the collection should look something like:
Illuminate\Database\Eloquent\Collection {#774 ▼
#items: array:1 [▼
"01.25" => 0,
"01.24" => 0,
"01.23" => 0,
"01.22" => 0,
"01.20" => 0,
"01.19" => 4,
...,
]
}
Any idea how to include the 0 amounts too?
I think you can use CarbonPeriod to create the calendar and set the default count 0 for every date.
And then reset the value from your users' count:
$start_date = explode(' ', User::whereDate('created_at', '>=', now()->subDays(7))->min('created_at'))[0];
$end_date = \Carbon\Carbon::now()->format('Y-m-d');
$period = \Carbon\CarbonPeriod::create($start_date, $end_date);
$all_dates = [];
foreach ($period as $date) {
$all_dates = array_merge($all_dates, [$date->format('m.d') => 0]);
}
$all_dates = array_reverse($all_dates);
collect($all_dates)->mapWithKeys(function($v, $date) use ($signUpsLastWeek) {
if (in_array($date, array_keys($signUpsLastWeek))) {
return [$date => $signUpsLastWeek[$date]->count()];
} else {
return [$date => 0];
}
})->all();

Eloquent query gets no result, but on database it works

Laravel gets no result for this function:
public function freigabenabruf()
{
DB::connection()->enableQueryLog();
$freigaben=DB::table('freigaben AS fg')
->join('TFI_RECHNUNG AS re', 're.re_nr_intern', 'fg.re_nr_intern')
->select( 'fg.freigabe1', 'fg.freigabe2', 'fg.created_at', 'fg.updated_at')
->orderBy('fg.created_at', 'desc')
->get();
var_dump($freigaben);
dd(DB::getQueryLog());
return $freigaben;
}
QueryLog retrieves:
object(Illuminate\Support\Collection)#293 (1) { ["items":protected]=> array(0) { } }
array:1 [▼
0 => array:3 [▼
"query" => "select "FG"."FREIGABE1", "FG"."FREIGABE2", "FG"."CREATED_AT", "FG"."UPDATED_AT" from "FREIGABEN" fg inner join "TFI_RECHNUNG" re on "RE"."RE_NR_INTERN" = "FG"." ▶"
"bindings" => []
"time" => 7.76
]
]
When I execute the query on database, I get my expected results...
Whats wrong? Any suggestions?
You have used select() two times. Try by using single select()
$freigaben=DB::table('freigaben AS fg')
->join('TFI_RECHNUNG AS re', 're.re_nr_intern', 'fg.re_nr_intern')
->select('re_nr', 'freigabe1', 'freigabe2', 'created_at', 'updated_at','fg.freigabe1', 'fg.freigabe2', 'fg.created_at', 'fg.updated_at')
->orderBy('fg.created_at', 'desc')
->get();

Codeigniter Multiple Join Not Getting Sub Cat "Name" with Group_Concat

On my list below I have all my categories displayed, but the sub categories do not display name. The sub category names should display like
Example
Example > Some Example 1
Example > Some Example 2
PHP
PHP > Functions
Question: On my model I should be able to retrieve the sub category name and the parent category name. But not working. Can only display parent name as shown in image I use group concat but it not show up in list group concat should display with > when sub cat name is there.
Why is it not working and what is the best solution?
As shown on image not display the > Some Example 1.
On my model I am using multiple join tables and I use for example .c1 and .cd1 get's parent categories and .cd2 .c2 gets sub categories.
But when I use my db->select with Group Concat it does not work and only gets the parent category name.
public function get_categories() {
$language_id = '1';
$this->db->select('cp.category_id AS category_id, GROUP_CONCAT(cd1.name SEPARATOR " > ") as name');
$this->db->from('category_path cp', 'LEFT');
// Parent Cat
$this->db->join('category c1', 'c1.category_id = cp.category_id', 'LEFT');
// Sub Cat
$this->db->join('category c2', 'c2.category_id = cp.path_id', 'LEFT');
// Parent Cat
$this->db->join('category_description cd1', 'cd1.category_id = cp.path_id', 'LEFT');
// Sub Cat
$this->db->join('category_description cd2', 'cd2.category_id = cp.category_id', 'LEFT');
$this->db->group_by('cp.category_id');
$this->db->order_by('name', 'ASC');
$this->db->where('cd1.language_id', (int)$language_id);
$this->db->where('cd2.language_id', (int)$language_id);
$query = $this->db->get();
return $query->result_array();
}
Tried with false below not worked same result
$this->db->select('cp.category_id AS category_id, GROUP_CONCAT(cd1.name SEPARATOR " > ") as name', false);
Var dump of results
array(5) { [0]=> array(2) { ["category_id"]=> string(2) "19" ["name"]=> string(7) "Example" } [1]=> array(2) { ["category_id"]=> string(2) "22" ["name"]=> string(7) "Example" } [2]=> array(2) { ["category_id"]=> string(2) "25" ["name"]=> string(7) "Example" } [3]=> array(2) { ["category_id"]=> string(2) "23" ["name"]=> string(3) "PHP" } [4]=> array(2) { ["category_id"]=> string(2) "24" ["name"]=> string(3) "PHP" } }
LAST QUERY
SELECT `cp`.`category_id` AS `category_id`, GROUP_CONCAT(cd1.name ORDER BY level SEPARATOR " > ") as name FROM `category_path` `cp` LEFT JOIN `category` `c1` ON `c1`.`category_id` = `cp`.`category_id` LEFT JOIN `category` `c2` ON `c2`.`category_id` = `cp`.`path_id` LEFT JOIN `category_description` `cd1` ON `cd1`.`category_id` = `cp`.`path_id` LEFT JOIN `category_description` `cd2` ON `cd2`.`category_id` = `cp`.`category_id` WHERE `cd1`.`language_id` = 1 AND `cd2`.`language_id` = 1 GROUP BY `cp`.`category_id` ORDER BY `name` ASC
Category Table
Category Desc Table
Category Path Table
Try this :
$this->db->select("cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name, c1.parent_id, c1.sort_order", FALSE);
$this->db->select() accepts an optional second parameter. If you set it to FALSE, CodeIgniter will not try to protect your field or table names with backticks. This is useful if you need a compound select statement.
Thanks to all who shared idea's to help.
I have found the cause of the issue is on my add category function.
When ever I create a category if it is a sub category in my category_path table
If sub category id is lets say 22 and parent id 19
Then on category_path table row should have two row should of inserted two rows like below.
category_id path_id level
22 19 0
22 22 1
For some reason on my model add category function it's not inserting the path id correct
Now I know what need to fix on model function.
Full Working Model
<?php
class Model_category extends CI_Model {
public function add() {
$data = array(
'parent_id' => (int)$this->input->post('parent_id'),
'status' => $this->input->post('status'),
'date_edited' => mdate('%Y-%m-%d %H:%i:%s', now()),
'date_added' => mdate('%Y-%m-%d %H:%i:%s', now())
);
$this->db->insert($this->db->dbprefix . 'category', $data);
$category_id = $this->db->insert_id();
$data = array(
'category_id' => (int)$category_id,
'language_id' => (int)'1',
'name' => $this->input->post('name'),
'meta_title' => $this->input->post('meta_title'),
'meta_description' => $this->input->post('meta_description'),
'meta_keyword' => $this->input->post('meta_keyword'),
'date_edited' => mdate('%Y-%m-%d %H:%i:%s', now()),
'date_added' => mdate('%Y-%m-%d %H:%i:%s', now())
);
$this->db->insert($this->db->dbprefix . 'category_description', $data);
// MySQL Hierarchical Data Closure Table Pattern
$level = 0;
$this->db->select('*');
$this->db->from($this->db->dbprefix . 'category_path');
$this->db->where('category_id', (int)$this->input->post('parent_id'));
$this->db->order_by('level', 'ASC');
$query = $this->db->get();
foreach ($query->result_array() as $result) {
$data = array(
'category_id' => (int)$category_id,
'path_id' => (int)$result['path_id'],
'level' => (int)$level,
);
$this->db->insert($this->db->dbprefix . 'category_path', $data);
$level++;
}
// The problem was here was not adding category into model but now
// All Fixed.
$data = array(
'category_id' => (int)$category_id,
'path_id' => (int)$category_id,
'level' => (int)$level
);
$this->db->insert($this->db->dbprefix . 'category_path', $data);
if (isset($data['keyword'])) {
$data = array(
'category_id' => 'category_id=' . (int)$category_id,
'keyword' => $this->input->post('keyword'),
'date_edited' => mdate('%Y-%m-%d %H:%i:%s', now()),
'date_added' => mdate('%Y-%m-%d %H:%i:%s', now())
);
$this->db->insert($this->db->dbprefix . 'url_alias', $data);
$this->db->query("INSERT INTO " . $this->db->dbprefix . "url_alias SET query = 'category_id=" . (int)$category_id . "', keyword = '" . $this->db->escape($data['keyword']) . "'");
}
}
public function edit() {
}
public function get_categories($data = array()) {
$language_id = '1';
$this->db->select('cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY level SEPARATOR " > ") as name, c1.parent_id, c1.sort_order');
$this->db->distinct();
$this->db->from($this->db->dbprefix . 'category_path cp', 'LEFT');
$this->db->join($this->db->dbprefix . 'category c1', 'cp.category_id = c1.category_id', 'LEFT');
$this->db->join($this->db->dbprefix . 'category c2', 'cp.path_id = c2.category_id', 'LEFT');
$this->db->join($this->db->dbprefix . 'category_description cd1', 'cp.path_id = cd1.category_id', 'LEFT');
$this->db->join($this->db->dbprefix . 'category_description cd2', 'cp.category_id = cd2.category_id', 'LEFT');
$this->db->where('cd1.language_id', '1');
$this->db->where('cd2.language_id', '1');
$this->db->group_by('cp.category_id');
$this->db->order_by('name', 'ASC');
$query = $this->db->get();
if ($query->num_rows() > 0) {
return $query->result_array();
} else {
return FALSE;
}
}
}

Resources