I have query where I am getting the sum of price by suppliers. The below query works fine.
private function integrationsSpendBySupplier(array $suppliers)
{
$totalBySupplier = DB::table('analytics')
->whereIn('source', $suppliers)->select(
'source',
DB::raw('sum(price) as total')
)
->groupBy('source')
->get();
return [
'title' => 'Spend per Supplier',
'rows' => 12,
'type' => 'bar',
'data' => [
'labels' => $totalBySupplier->map(fn ($supplier) => $supplier->source),
'datasets' => [
[
'label' => 'Total Spending',
'data' => $totalBySupplier->map(fn ($supplier) => $this->stringToFloat($supplier->total))
],
]
],
'hasToolTip' => true
];
}
However, I would also like to add more items to the DB::raw() call like so:
DB::raw('sum(price + shipping + tax + something_else) as total')
But these values will be dynamic so it could be all or none of additional params (price will always be there though).
Any thoughts?
You can build the SUM() string using basic PHP string and apply that string to the query.
for example
$price = '';
if (priceRequired())
$price = "price ";
$shipping = '';
if (requiresShipping())
$shipping = ", shipping";
// $shipping = requiresShipping() ? ", shipping" : ""; if you're familiar with this syntax
...
DB::raw("sum($price $shipping ...etc) as total");
as a result, the final string should be like price, shipping, tax or price depending on the above conditions.
Note: be careful about the comma's place in the string, this may cause query exceptions
Related
Let's say I've got eloquent collection returns from:
$b = Book::get(); (columns: id, book_name, pages)
$m = Magazine::get(); (columns, id, mag_name, type)
How do I then:
Combine them in the same collection.
Rename ->book_name/mag_name to k
Add type and if book use 'null' as value
add ->hash (str::random)
I want to keep the collection to be able to use the benifits that come with it. ie. not convert it to an array.
Laravel 7
You can do it with collection merge method
$b = Book::get(['id','book_name as k','pages']);
$m = Magazine::get(['id','mag_name as k','type']);
$bb = $b->map(function ($book){
$book['type'] = null;
$book['hash'] = Str::random();
return $book;
});
$mm = $m->map(function ($magzine){
$magzine['hash'] = Str::random();
return $magzine;
});
$merged = $bb->merge($mm);
return $merged;
Assume we have the following collections:
$b = Book::get(); //(columns: id, book_name, pages)
$m = Magazine::get(); //(columns, id, mag_name, type)
Second, update the fields:
$books = $b->map(function ($book) => {
return [
'id' => $book->id,
'k' => $book->book_name,
'type' => null,
'hash' => Str::random()
];
});
$magazines = $b->map(function ($magazine) => {
return [
'id' => $magazine->id,
'k' => $magazine->mag_name,
'type' => $magazine->type,
'hash' => Str::random()
];
});
Third, merge the collections:
$result = $books->merge(magazines);
I have a sql request concerning admin :
$admin = Admin::findOrFail(Auth::guard('admin')->user()->id);
from this sql request I also manage to get the users of the admin ...
$users = $admin->users;
Each users must follow training sessions. and I must calculate it by using SQL requests for each users ... So i wrote this foreach statement
foreach ($users as $user) {
$todo = User::select() .....where('users.id' = $user->id)->get();
$done = User::select() .....where('users.id' = $user->id)->get();
$totalTimes = $todo->toBase()->sum('dureeformation');
$spendTimes = $done->toBase()->sum('dureeformation');
$remainingTimes = $totalTimes - $spendTimes;
$timeData[] = ['id' => $user->id, 'totalTime' => $totalTimes, 'spendTime' => $spendTimes, 'remainingTime' => $remainingTimes];
}
the totalTimes, spendTimes and remainingTimes are operations on Collections and i get expected results ...
$users->each(function ($record) use ($timeData) {
$record['totalTime'] = $timeData[$record['id']]['totalTime'];
$record['spendTime'] = $timeData[$record['id']]['spendTime'];
$record['remainingTime'] = $timeData[$record['id']]['remainingTime'];
//dd($record['totalTime']);
});
After this :
$timeData = collect($timeData);
$timeData= $timeData->keyBy('id');
$users = collect($users->toArray());
I have an issue here :
$users->each(function ($record) use ($timeData) {
$record['totalTime'] = $timeData[$record['id']]['totalTime'];
$record['spendTime'] = $timeData[$record['id']]['spendTime'];
$record['remainingTime'] = $timeData[$record['id']]['remainingTime'];
//var_dump($record);
});
$record in the function gives me what i expect ... the var_dump($records) added columns where it was expected but i can't take those results out of the function.
I tried to do this :
$variable = $users->each(function ($record) use ($timeData)...
dd($variable)
but unsuccessfully ...
Looks to me like your $timeData array is built differently than you access it. In your code you are doing
$timeData[] = [
'id' => $user->id,
'totalTime' => $totalTimes,
'spendTime' => $spendTimes,
'remainingTime' => $remainingTimes,
];
which will produce an array like this
[
0 => [
'id' => 17,
'totalTime' => 5,
'spendTime' => 4,
'remainingTime' => 3
],
1 => [
'id' => 17,
'totalTime' => 7,
'spendTime' => 5,
'remainingTime' => 2
]
]
but what you actually want is to have the id as index, right? Then you'd have to build the array this way:
$timeData[$user->id] = [
'totalTime' => $totalTimes,
'spendTime' => $spendTimes,
'remainingTime' => $remainingTimes,
];
or, as an alternative, perform a different lookup:
$users->each(function ($record) use ($timeData) {
$times = array_first($timeData, function ($value, $key) use ($record) {
return $value['id'] === $record['id'];
});
$record['totalTime'] = $times['totalTime'];
$record['spendTime'] = $times['spendTime'];
$record['remainingTime'] = $times['remainingTime'];
});
Both solutions have pros and cons, though. On the one hand, using the user identifier as the index will produce a huge array that is only filled partially. On the other hand, the second solution is a bit slower. As long as you are not going to deal with thousands of entries in the $timeData array, this won't be an issue, though.
For example i have "id, name, wage, sex, age" columns.
1, John, 3, M, 30
2, Angela, 5, F, 26
If i have 50 rows like this. And if i want to save name, wage into table1 & sex and age into table2. In laravel docs queries/insert, they told us make an array and put values on it. But how should i put some of the values into table1 and other values into table2 in same foreach.
foreach($test as $tests)
{
$data[] =[
'name' => $tests->name,
'wage' => $tests->wage,
'sex' => $tests->sex,
'age' => $tests->age
];
}
Products::insert($data);
Is this the right ways to do it? I cant figure out the correct way to do.
If these tables are not related, you can do it with just 2 queries:
foreach ($tests as $test) {
$products[] = [
'name' => $tests->name,
'wage' => $tests->wage
];
$otherData[] = [
'sex' => $tests->sex,
'age' => $tests->age
];
}
Products::insert($products);
OtherModel::insert($otherData);
In case if these models are related, you'll need to create 51 query instead of 2 (still better than 100 queries):
foreach ($tests as $test) {
$productId = Products::insertGetId([
'name' => $tests->name,
'wage' => $tests->wage,
]);
$otherData[] = [
'sex' => $tests->sex,
'age' => $tests->age,
'product_id' => $productId
];
}
OtherModel::insert($otherData);
If these models are related and you still want to do this with just a few queries, you could use transactions:
DB::transaction(function () {
$productId = (int)DB::table('products')->orderBy('id', 'desc')->value('id');
foreach ($tests as $test) {
$productId++;
$products[] = [
'name' => $tests->name,
'wage' => $tests->wage
];
$otherData[] = [
'sex' => $tests->sex,
'age' => $tests->age,
'product_id' => $productId
];
}
Products::insert($products);
OtherModel::insert($otherData);
});
you could loop trough data and insert into DB table.
foreach($test as $tests)
{
$product = new Products();
$product->name = $tests->name;
$product->name = $tests->name;
$product->save();
$another = new AnotherTableModel();
$another->sex= $tests->sex;
$another->age= $tests->age;
$another->save();
}
CakePHP Version 3.5.5
My end goal is to provide the user the functionality to change the amount of results displayed via a select list on the index view. Also I need the initial page load to be sorted by area_name asc.
// WHAT I'VE DONE
I changed where I was stipulated the limit parameter which can be seen below.
// AREAS CONTROLLER
public $paginate = [
'sortWhitelist' => [
'Areas.area_name', 'Users.first_name', 'Users.last_name'
]
//'limit' => 1, // REMOVED FROM HERE
//'order' => [ // REMOVED FROM HERE
//'Areas.area_name' => 'asc'
//]
];
public function index()
{
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1]);
$limit = 1;
$this->paginate = [
'order' => ['Areas.area_name' => 'asc'], // ADDED HERE
'limit' => $limit // ADDED HERE
];
$this->set('areas', $this->paginate($query));
}
And I declare the pagination sort links like:
// AREAS INDEX VIEW
<?= $this->Paginator->sort('Areas.area_name', __('Area Name')) ?>
<?= $this->Paginator->sort('Users.first_name', __('First Name')) ?>
<?= $this->Paginator->sort('Users.last_name', __('Last Name')) ?>
// RESULT
The above code works on all index methods within the application that don't use contain but when I implemented this solution here everything worked except I cannot sort on the associated data - IE: Users first and last name?
=========================================================================
WHAT I'VE TRIED
// Attempt 1
I added an initialize method above the public $paginate class like:
public function initialize()
{
$limit = 1;
}
public $paginate = [
'sortWhitelist' => [
'Areas.area_name', 'Users.first_name', 'Users.last_name'
]
'limit' => $limit,
'order' => [
'Areas.area_name' => 'asc'
]
];
public function index()
{
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1]);
$this->set('areas', $this->paginate($query));
}
And the view I left the same.
// Result for Attempt 1
syntax error, unexpected ''limit'' (T_CONSTANT_ENCAPSED_STRING), expecting ']' on line 36 which is 'limit' => $limit,
=========================================================================
// Attempt 2
I tried to add the limit parameter and order array to the query like:
public function index()
{
$limit = 1;
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1])
->order(['Areas.area_name' => 'asc'])
->limit($limit);
$this->set('areas', $this->paginate($query));
}
// Result for Attempt 2
The result set was not ordered by the area_name and not limited to 1 result.
=========================================================================
// Attempt 3
I then changed the query and tried the following just to see if I could get a dynamic limit working:
$limit = 1;
$query = $this->Areas->find('all')
->contain('Users', function ($q) {
return $q
//->order('Areas.area_name' => 'asc'),
->limit($limit);
})
->where(['Areas.status' => 1]);
$this->set('areas', $this->paginate($query));
// Result for Attempt 3
The result set was not limited to 1 result.
=========================================================================
ADDITIONAL INFORMATION
// USERS TABLE
$this->hasOne('Areas', [
'foreignKey' => 'user_id'
]);
// AREAS TABLE
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
I searched through the following cookbook sections (Pagination, Query Builder, Retrieving Data & Result Sets and Associations - Linking Tables Together) but I can't find a way to get this working so any help would be much appreciated.
Many thanks. Z.
You are overwriting the $paginate property in your index() method, so your settings including the whitelist are being lost.
Set the keys directly instead:
$this->paginate['order'] = ['Areas.area_name' => 'asc'];
$this->paginate['limit'] = $limit;
Here is the code what I've tried
$conditions = "category = :id: AND status = :status: ORDER BY :order: LIMIT 3";
$parameters = array(
"id" => $cat_id,
"status" => 1,
"order" => "title ASC",
);
$posts = Posts::find(array(
$conditions,
"bind" => $parameters
));
Everything is fine except order by. Can you please help me to find out the proper way to use order by in PhalconPHP?
"bind" and "order" are separate parameters:
$conditions = "category = :id: AND status = :status:";
$parameters = array(
"id" => $cat_id,
"status" => 1,
);
$posts = Posts::find(array(
"conditions" => $conditions,
"bind" => $parameters,
"order" => "title ASC",
"limit" => 3
));
this is clean and reliable approach - this way you have in your conditions only striclty conditions. You can change your limit/order based on request params without touching condition string.