How to turn string output from sql to integer? - sql

I am generating charts using google chart api. I have done it locally and successfully produce the charts. However, when I transfer them to server (cpanel) the charts is ruined. I have detected that the output of sql query for generating the charts value is produced in string format with "6".
This is the code to get the charts' value:
public function gender()
{
$data = DB::table('results')
->select(
DB::raw('gender as gender'),
DB::raw('count(*) as number'))
->groupBy('gender')
->get();
//dd($data);
$array[] = ['Gender', 'Number'];
foreach($data as $key => $value)
{
$array[++$key] = [$value->gender, $value->number];
}
//dd($array);
return view('gender')->with('gender', json_encode($array));
}
In my local, I try to access the data of the sql using dd($data); producing:
Collection {#263 ▼
#items: array:2 [▼
0 => {#264 ▼
+"gender": "female"
+"number": 6
}
1 => {#266 ▼
+"gender": "male"
+"number": 6
}
]
}
I tried accessing in server using same dd($data);
Collection {#260 ▼
#items: array:2 [▼
0 => {#261 ▼
+"gender": "female"
+"number": "6"
}
1 => {#263 ▼
+"gender": "male"
+"number": "6"
}
]
The difference is the number value from server code is in string.
Why this happened and how to fix the problem?

I had a similar issue. It seems it's a PHP issue. Are you running the same PHP versions on local and production?
In any case, you could also explicitly cast it as an UNSIGNED integer. I was able to do that with the following:
$data = DB::table('results')
->selectRaw('gender as gender, CAST(count(*) AS UNSIGNED) as number')
->groupBy('gender')
->get();

Related

how to append user input to Http get request in Laravel 9

Thank you in advance for taking the time to read through my question.
I'm upgrading an old project from Laravel 8 to Laravel 9
in laravel 8 i was able to append user input to a get request like this :
public function fetch(Request $request)
{
$userInput = $request->input();
$response = Http::withHeaders(
[
"x-rapidapi-host"=> "some.api",
"x-rapidapi-key"=> "**************",
]
)->get("https://some.api.com",$userInput);
$data = json_decode($response,true);
return view('view',compact('data'));
}
but when i try to do the same process in Laravel 9 i'm getting an error:
Invalid request
edit: (using dd() to show more error details about the response I'm getting)
array:4 [▼ // app\Http\Controllers\apiController.php:29
"searchType" => "Title"
"expression" => null
"results" => []
"errorMessage" => "Invalid request. See more information at https://theapiI'mUsing.com/api#/{lang?}/API/Search/{expression} - Search APIs: Search, SearchTitle, SearchName, SearchEpisod ▶"
]
I'm not able to send the data to the controller i guess? expression is showing null
although I was able to accomplish my goal using the same code in laravel 8
I have tried to append the input like this:
$response = Http::get("https://some.api.com/apiKey", [
'input' => $userInput,
]);
but also got the same error!
Invalid request
edit:
I have Also tried to concatenate the url with the user input
but got this error:
Array to string conversion
What is the proper way to append user input to a get request in Laravel 9?

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

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.

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()),
],
},

ElasticSearch analyzed fields

I'm building my search but need to analyze 1 field with different analyzers. My problem is for a field I need to have an analyzer on it for stemming (snowball) and then also one to keep the full word as one token (keyword). I can get this to work by the following index settings:
curl -X PUT "http://localhost:9200/$IndexName/" -d '{
"settings":{
"analysis":{
"analyzer":{
"analyzer1":{
"type":"custom",
"tokenizer":"keyword",
"filter":[ "standard", "lowercase", "stop", "snowball", "my_synonyms" ]
}
}
},
"filter": {
"my_synonyms": {
"type": "synonym",
"synonyms_path ": "synonyms.txt"
}
}
}
},
"mappings": {
"product": {
"properties": {
"title": {
"type": "string",
"search_analyzer" : "analyzer1",
"index_analyzer" : "analyzer1"
}
}
}
}
}';
The problem comes when searching on a single word in the title field. If it's populated with The Cat in the Hat it will store it as "The Cat in the Hat" but if I search for cats I get nothing returned.
Is this even possible to accomplish or do I need to have 2 separate fields and analyze one with keyword and the other with snowball?
I'm using nest in vb code to index the data if that matters.
Thanks
Robert
You can apply two different analyzers to the same using the fields property (previously known as multi fields).
My VB.NET is a bit rusty, so I hope you don't mind the C# examples. If you're using the latest code from the dev branch, Fields was just added to each core mapping descriptor so you can now do this:
client.Map<Foo>(m => m
.Properties(props => props
.String(s => s
.Name(o => o.Bar)
.Analyzer("keyword")
.Fields(fs => fs
.String(f => f
.Name(o => o.Bar.Suffix("stemmed"))
.Analyzer("snowball")
)
)
)
)
);
Otherwise, if you're using NEST 1.0.2 or earlier (which you likely are), you have to accomplish this via the older multi field type way:
client.Map<Foo>(m => m
.Properties(props => props
.MultiField(mf => mf
.Name(o => o.Bar)
.Fields(fs => fs
.String(s => s
.Name(o => o.Bar)
.Analyzer("keyword"))
.String(s => s
.Name(o => o.Bar.Suffix("stemmed"))
.Analyzer("snowball"))
)
)
)
);
Both ways are supported by Elasticsearch and will do the exact same thing. Applying the keyword analyzer to the primary bar field, and the snowball analyzer to the bar.stemmed field. stemmed of course was just the suffix I chose in these examples, you can use whatever suffix name you desire. In fact, you don't need to add a suffix, you can name the multi field something completely different than the primary field.

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}";