Laravel Excel: how to have all zeroes printed in a exported csv? - laravel-excel

I'm exporting to csv using Laravel Excel.
I'm creating the out value as
'amount' => number_format(-$ai->final_invoice_amount, 2, ".", "")
But, for example for 204.00, in the exported csv I got only 204, without dot and leading zeroes.
I know that it's a valid number for a computer; but our client has a strict parser and it wants a 204.00 value.
I tried, but not works, to add an explicit cast to string, but it's useless because number_format outputs a string in any case
'amount' => (string)number_format(-$ai->final_invoice_amount, 2, ".", "")

Laravel Excel's Events give you access to PHPSpreadSheet under the hood.
https://docs.laravel-excel.com/3.1/exports/extending.html#events
With Events, you can apply the desired formatting to a cell or group of cells. It's easier to demonstrate than explain, so I have created a simple example.
<?php
namespace App\Export;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeSheet;
class SampleExport implements FromCollection, WithEvents
{
public function collection()
{
return collect([
[
'id' => 1,
'amount_1' => 100,
'amount_2' => 75.20,
'amount_3' => -23.10,
],
[
'id' => 2,
'amount_1' => -60,
'amount_2' => 50.40,
'amount_3' => 110,
],
]);
}
public function registerEvents(): array
{
return [
BeforeSheet::class => function (BeforeSheet $event) {
// format columns B-D to two decimal places
$event->sheet
->getDelegate()
->getStyle('B:D')
->getNumberFormat()
->setFormatCode('0.00');
},
];
}
}
This is the resulting CSV as output to a file.
"1","100.00","75.20","-23.10"
"2","-60.00","50.40","110.00"
NOTE: The values being formatted MUST BE A NUMBER TYPE! Attempting to format strings will not work.

Related

Laravel 8 save integer into null DB column - save vs update

I'm having an interesting issue where Laravel's save() does not replace null values in database, while the update() does. I also tested by manually changing null to 1 and afterwards the save() started updating the column. I am using save() throughout the project and would prefer that it stays that way or that at least I understand the problem before I go changing the code.
Migration (all codes will be partial extracts for brewity):
$table->foreignId('checklist_id')->nullable()->constrained('checklists')->onUpdate('cascade')->onDelete('cascade');
$table->foreignId('action_id')->nullable()->constrained('actions')->onUpdate('cascade')->onDelete('cascade');
Model (added the checklist_id and action_id for testing, otherwise they are not here):
// test
protected $fillable = [
'checklist_id',
'action_id',
];
protected $casts = [
'checklist_id' => 'integer',
'action_id' => 'integer',
];
// actual
protected $fillable = [
];
protected $casts = [
];
Controller:
public function update(UpdateWorkOrderRequest $request, WorkOrder $workOrder)
{
$response = $workOrder->save($request->all());
}
Form submission (from Chrome):
------WebKitFormBoundary73Z4wzsmsGcgOOAM
Content-Disposition: form-data; name="checklist_id"
1
------WebKitFormBoundary73Z4wzsmsGcgOOAM
Content-Disposition: form-data; name="action_id"
1
dd($request->all()):
array:2 [▼
"checklist_id" => "1"
"action_id" => "1"
]
The fields checklist_id and action_id are null after using save(), but they do get updated using update(). It's also working if I do this:
$workOrder->checklist_id = $request->checklist_id;
$workOrder->action_id = $request->action_id;
$workOrder->save();
I opted for save() to write less code instead of above example and because from docs I presume I can use save() when assigning data from $request, like here: https://laravel.com/docs/9.x/eloquent#updates
As discussed in the comments, you're trying to use model->save(attributes).
The save function does not provide any parameters for this. (docs).
You have two options, either use the model->update function (docs):
$workOrder->update($request->all());
Or use the model->fill (docs) function in combination with model->save like so:
$workOrder->fill($request->all())->save();

Sequelize raw query update array of objects as replacements

I am using sequelize (postgres) and I need to properly escape a query like this:
`
UPDATE "Pets"
SET "name" = CASE LOWER("name")
${input.pets
.map((pet) => `WHEN '${pet.name.toLowerCase()}' THEN '${pet.newName}'`)
.join('\n')}
ELSE "name"
END
WHERE LOWER("name") IN(${input.pets
.map((pet) => `'${pet.name.toLowerCase()}'`)
.join(',')});
`
Sample input.pets:
[{ name: "rocky", newName: "leo" }]
Does anyone have an idea how to achieve this with replacements?
I have found a thread on github which suggested something like this:
let data = [ [ 252456, 1, 55, '0' ],
[ 357083, 1, 56, '0' ],
[ 316493, 1, 57, '0' ] ];
db.query(
`INSERT INTO product (a, b) VALUES ${data.map(a => '(?)').join(',')};`,
{
replacements: data,
type: Sequelize.QueryTypes.INSERT
}
);
However, a 2d array is being used here not an array of objects. Is there a way to access individual properties from the array? When I try something like this
`
UPDATE "Pets"
SET "name" = CASE LOWER("name")
${input.pets
.map((_pet) => `WHEN ? THEN ?`)
.join('\n')}
ELSE "name"
END
WHERE LOWER("name") IN(${input.pets
.map((_pet) => `?`)
.join(',')});
`,
{ type: QueryTypes.UPDATE, replacements: input.pets },
The first ? turns out to be the whole object. Is there a way to access it's properties?
I also tried transforming input.pets into a 2d array but still couldn't get it to work as in example with insert above.
In advance thanks for your time
const names = input.pets.map((pet) => pet.name);
const newNames = input.pets.map((pet) => pet.newName);
`
UPDATE "Pets"
SET "name" = CASE LOWER("name")
${names.map((_) => `WHEN LOWER(:names) THEN :newNames`).join('\n')}
ELSE "name"
END
WHERE LOWER("name") IN(${names.map((_) => `LOWER(:names)`).join(',')});
`,
{ replacements: { names, newNames } },
This works. In cases like this it's better to work with simpler data structures. Another option I found is using sequelize.escape() built-in function, but it's not documented so I decided not to
EDIT:
After some testing, this works but for only one object in the input
If the input looks something like this:
[
{ name: "rocky", newName: "fafik" }
{ name: "asd", newName: "qwerty" }
]
Then in resut I get queries like this:
WHEN LOWER('rocky', 'asd') THEN 'fafik', 'qwerty'
WHEN LOWER('rocky', 'asd') THEN 'fafik', 'qwerty'
So it doesn't loop over arrays. Still the problem remains, how to access individual properties, whether from array or an object?
EDIT2: FINAL ANSWER
sequelize.query(
`
UPDATE "Pets"
SET "name" = CASE LOWER("name")
${input.pets.map(() => `WHEN ? THEN ?`).join('\n')}
ELSE "name"
END
WHERE LOWER("name") IN(?);
`,
{
replacements: [
...input.pets.flatMap((x) => [x.name.toLocaleLowerCase(), x.newName]),
input.pets.map((x) => x.name.toLocaleLowerCase()),
],
},

Ramda.js - how to view many values from a nested array

I have this code:
import {compose, view, lensProp, lensIndex, over, map} from "rambda";
let order = {
lineItems:[
{name:"A", total:33},
{name:"B", total:123},
{name:"C", total:777},
]
};
let lineItems = lensProp("lineItems");
let firstLineItem = lensIndex(0);
let total = lensProp("total");
My goal is to get all the totals of all the lineItems (because I want to sum them). I approached the problem incrementally like this:
console.log(view(lineItems, order)); // -> the entire lineItems array
console.log(view(compose(lineItems, firstLineItem), order)); // -> { name: 'A', total: 33 }
console.log(view(compose(lineItems, firstLineItem, total), order)); // -> 33
But I can't figure out the right expression to get back the array of totals
console.log(view(?????, order)); // -> [33,123,777]
That is my question - what goes where the ????? is?
I coded around my ignorance by doing this:
let collector = [];
function collect(t) {
collector.push(t);
}
over(lineItems, map(over(total, collect)), order);
console.log(collector); // -> [33,123,777]
But I'm sure a ramda-native knows how to do this better.
It is possible to achieve this using lenses (traversals), though will likely not be worth the additional complexity.
The idea is that we can use R.traverse with the applicative instance of a Const type as something that is composable with a lens and combines zero or more targets together.
The Const type allows you to wrap up a value that does not change when mapped over (i.e. it remains constant). How do we combine two constant values together to support the applicative ap? We require that the constant values have a monoid instance, meaning they are values that can be combined together and have some value representing an empty instance (e.g. two lists can be concatenated with the empty list being the empty instance, two numbers can be added with zero being the empty instace, etc.)
const Const = x => ({
value: x,
map: function (_) { return this },
ap: other => Const(x.concat(other.value))
})
Next we can create a function that will let us combine the lens targets in different ways, depending on the provided function that wraps the target values in some monoid instance.
const foldMapOf = (theLens, toMonoid) => thing =>
theLens(compose(Const, toMonoid))(thing).value
This function will be used like R.view and R.over, accepting a lens as its first argument and then a function for wrapping the target in an instance of the monoid that will combine the values together. Finally it accepts the thing that you want to drill into with the lens.
Next we'll create a simple helper function that can be used to create our traversal, capturing the monoid type that will be used to aggregate the final target.
const aggregate = empty => traverse(_ => Const(empty))
This is an unfortunate leak where we need to know how the end result will aggregated when composing the traversal, rather than simply knowing that it is something that needs to be traversed. Other languages can make use of static types to infer this information, but no such luck with JS without changing how lenses are defined in Ramda.
Given you mentioned that you would like to sum the targets together, we can create a monoid instance that does exactly that.
const Sum = x => ({
value: x,
concat: other => Sum(x + other.value)
})
This just says that you can wrap two numbers together and when combined, they will produce a new Sum containing the value of adding them together.
We now have everything we need to combine it all together.
const sumItemTotals = order => foldMapOf(
compose(
lensProp('lineItems'),
aggregate(Sum(0)),
lensProp('total')
),
Sum
)(order).value
sumItemTotals({
lineItems: [
{ name: "A", total: 33 },
{ name: "B", total: 123 },
{ name: "C", total: 777 }
]
}) //=> 933
If you just wanted to extract a list instead of summing them directly, we could use the monoid instance for lists instead (e.g. [].concat).
const itemTotals = foldMapOf(
compose(
lensProp('lineItems'),
aggregate([]),
lensProp('total')
),
x => [x]
)
itemTotals({
lineItems: [
{ name: "A", total: 33 },
{ name: "B", total: 123 },
{ name: "C", total: 777 }
]
}) //=> [33, 123, 777]
Based on your comments on the answer from customcommander, I think you can write this fairly simply. I don't know how you receive your schema, but if you can turn the pathway to your lineItems node into an array of strings, then you can write a fairly simple function:
const lineItemTotal = compose (sum, pluck ('total'), path)
let order = {
path: {
to: {
lineItems: [
{name: "A", total: 33},
{name: "B", total: 123},
{name: "C", total: 777},
]
}
}
}
console .log (
lineItemTotal (['path', 'to', 'lineItems'], order)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {compose, sum, pluck, path} = R </script>
You can wrap curry around this and call the resulting function with lineItemTotal (['path', 'to', 'lineItems']) (order), potentially saving the intermediate function for reuse.
Is there a particular reason why you want to use lenses here? Don't get me wrong; lenses are nice but they don't seem to add much value in your case.
Ultimately this is what you try to accomplish (as far as I can tell):
map(prop('total'), order.lineItems)
you can refactor this a little bit with:
const get_total = compose(map(prop('total')), propOr([], 'lineItems'));
get_total(order);
You can use R.pluck to get an array of values from an array of objects:
const order = {"lineItems":[{"name":"A","total":33},{"name":"B","total":123},{"name":"C","total":777}]};
const result = R.pluck('total', order.lineItems);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

Flutter Printing Package: Create pdf table with loop

I want to create a pdf document with the package 'pdf'. The example on the dart - page is working fine: https://pub.dev/packages/pdf#-example-tab-
You can see that the table is static. I want to create a dynamic table in the pdf document.
The columns will be constant, but the rows have to be dynamic.
I have tried to insert a for() - loop.
The syntax is not correct.
pdfWidget.Table.fromTextArray(context: context, data: <List<String>> [
<String>['Date', 'PDF Version', 'Acrobat Version'],
//.....
//more Strings here.....
]),
I ran into the same issue.
This seemed to work for me.
pdf.addPage(
MultiPage(
build: (context) => [
Table.fromTextArray(context: context, data: <List<String>>[
<String>['Msg ID', 'DateTime', 'Type', 'Body'],
...msgList.map(
(msg) => [msg.counter, msg.dateTimeStamp, msg.type, msg.body])
]),
],
),
);
where my msgList object was a custom List, ie: List<SingleMessage>
There are several ways to do it, I prefer to fill the List separately, like:
List<List<String>> salidas = new List();
salidas.add(<String>['Title1','Title2', ... , 'Title n']);
for(var indice=0;indice<records.length;indice++) {
List<String> recind = <String>[
records[indice].Stringfield1,
records[indice].Stringfield2,
...,
records[indice].Stringfieldn
];
salidas.add(recind);
}
...
fpdf.Table.fromTextArray(context: context,data: salidas),

Getting SQL MIN() and MAX() from DBIx::Class::ResultSetColumn in one query

I want to select the MIN() and MAX() of a column from a table. But instead of querying the database twice I'd like to solve this in just one query.
I know I could do this
my $col = $schema->result_source("Birthday")->get_column("birthdate");
my $min = $col->min();
my $max = $col->max();
But it would query the database twice.
The only other solution I found is quite ugly, by messing around with the select and as attributes to search(). For example
my $res = $rs->search({}, {
select => [ {min => "birthdate"}, {max => "birthdate"},
as => [qw/minBirthdate maxBirthdate/]
});
say $res->get_column("minBirthdate")->first() . " - " . $res->get_column("maxBirthdate")->first();
Which produces this - my wanted SQL
SELECT MIN(birthdate), MAX(birthdate) FROM birthdays;
Is there any more elegant way to get this done with DBIx::Class?
And to make it even cooler, is there a way to respect the inflation/deflation of the column?
You can use columns as a shortcut to combine select and as attributes as such:
my $res = $rs->search(undef, {
columns => [
{ minBirthdate => { min => "birthdate" } },
{ maxBirthdate => { max => "birthdate" } },
]
});
Or, if you prefer more control over the SQL, use string refs, which can help with more complex calculations:
my $res = $rs->search(undef, {
columns => [
{ minBirthdate => \"MIN(birthdate)" },
{ maxBirthdate => \"MAX(birthdate)" },
]
});
Now, if you really want to clean it up a bit, I highly recommend DBIx::Class::Helpers, which allows you to write it as such:
my $minmax = $rs->columns([
{minBirthdate=>\"MIN(birthdate)"},
{maxBirthdate=>\"MAX(birthdate)"},
])->hri->single;
say "$minmax->{minBirthdate} - $minmax->{maxBirthdate}";