Get array with key and value as my select attributes in DQL - symfony

I'm doing a query using DQL
$query = $this->createQueryBuilder('i')
->select('(i.sortiesNoSortie)','COUNT(i.participantsNoParticipant)')
->groupBy('i.sortiesNoSortie');
$result = $query->getQuery()->getArrayResult();
but i'm getting this
array:3 [
0 => array:2 [
1 => "76"
2 => "1"
]
1 => array:2 [
1 => "82"
2 => "1"
]
2 => array:2 [
1 => "83"
2 => "1"
]
]
And I want to get that in a simple array with key and value from my select
76 => 1
82 => 1
83 => 1
Is that possible ?

For obvious reasons, the result is a 2D array as it is representing the rows and columns that you would receive when executing the query. From the top of my head, there isn't a doctrine provided function to transform it into your desired format.
You can however use PHP to reformat the array for you.
$result = $this->createQueryBuilder('i')
->select('(i.sortiesNoSortie)','COUNT(i.participantsNoParticipant)')
->groupBy('i.sortiesNoSortie')
->getQuery()
->getArrayResult();
$output = array();
foreach($result as $row) {
$output[$row[0]] = $row[1];
}
While it seems at first that this is not ideal, realistically if doctrine were to implement something to format it the way you want it, it would most likely do something similar and just wrap it in a nice function for you.
Having said that, there may be a nicer way to process the data using PHP other than a foreach loop. Either way, don't shy away completely from reformatting your data in PHP after getting it from doctrine. Obviously this would be a case by case thing but it seems that you are processing small amounts of data, so the extra processing won't be an issue.
Edit
Thanks #Matteo for reminding me. array_column is the function that you can use to replace the foreach loop.
$output = array_column($result, 1, 0);

Related

Does the symfony translation system support an arbitrary-length list of elements to be concatenated?

I need to translate variable-length sentences in Symfony 3.4.
Sample sentence: "Included: 4 breakfasts, 1 brunch and 3 dinners."
For the exemple let's say the following constraints:
I have to express the number of breakfasts, brunches, lunches and dinners that a travel may have in a full itinerary
The list of "things" to enumerate is known and finite. There is singular and plural translation for each of the key-words.
Not all of them have to appear. If there are 0 units, the full block will be omitted.
The list is comma-separated.
Except the last block which is added with an "and" connector.
If there's only 1 block there's no comma and no "and" connector.
Each can be singular/plural.
There are multiple languages, for this example I'll use English and Spanish, but in the reality there is also catalan.
The keywords are the following in english-singular / english-plural / spanish-singular / spanish-plural:
breakfast / breakfasts / desayuno / desayunos
brunch / brunches / alumerzo-desayuno / almuerzo-desayunos
lunch / lunches / almuerzo / almuerzos
dinner / dinners / cena / cenas
So... I can imagine the following tests cases:
[
'language' => 'en-US',
'parameters' => [
'breakfast' => 4,
'dinner' => 1,
],
'expectedOutput' => 'Included: 4 breakfasts and 1 dinner.'
],
[
'language' => 'es-ES',
'parameters' => [
'breakfast' => 2,
'brunch' => 1,
'lunch' => 5,
'dinner' => 2,
],
'expectedOutput' => 'Incluido: 2 desayunos, 1 desayuno-almuerzo, 5 almuerzos y 2 cenas.'
],
[
'language' => 'en-US',
'parameters' => [
'breakfast' => 1,
],
'expectedOutput' => 'Included: 1 breakfast.'
],
[
'language' => 'es-ES',
'parameters' => [
'dinner' => 4,
'lunch' => 3,
],
'expectedOutput' => 'Incluido: 3 almuerzos y 4 cenas.'
],
Questions
Is this supported by the native translation engine in Symfony 3.4?
If not, is this supported by the native translation engines of Symfony 4 or 5?
Otherwise, is there any known pattern or strategy that fits this case?
What the translation engine can do to some extent is use a number to switch between different cases (for singular/plural). Have a look at transChoice for symfony 3.4 (til 4.1.x apparently). Since symfony 4.2, the ICU message format (follow links too) may be used to handle pluralization.
However, (conditional) string building is not really a task for the translation engine at all (even though for simple cases transChoice or the ICU message format might work out fine). So you'll probably end up building the strings from bits and pieces.
Building the string in php
// fetch translations for the itemss
$items = [];
foreach($parameters as $key => $count) {
if($count <= 0) {
continue;
}
$items[] = $translator->transChoice($key, $count);
}
// handle 2+ items differently from 1 item
if(count($items) > 1) {
// get last item
$last = array_pop($items);
// comma-separate all other items, add last item with "and"
$itemstring = implode(', ', $items)
. $translator->trans('included_and')
. $last;
} else {
$itemstring = $items[0] ?? ''; // if no items or something like
// $translator->trans('included_none')
}
return $translator->trans('included', ['%items%' => $itemstring]);
and in the translation file (english):
'included' => 'Included: %items%.',
'included_and' => ' and ',
'breakfast' => '%count% breakfast|%count% breakfasts', // etc. for all other stuff
Please note, that transChoice should automatically set the %count% placeholder to the count provided as the second parameter to transChoice.
alternate approaches
You could easily split the translatable strings into breakfast_singular and breakfast_plural if you wanted to avoid the use of transChoice, you would have to replace
transChoice($key, $count)
with
trans($key.($count > 1 ? '_plural' : '_singular'), ['%count%' => $count])
You could also expand the included string into included_single and included_multiple where the latter is being translated as 'Included: %items% and %last%', with appropriate parameters and conditionals.
Ultimately though, string generation or more specifically language generation isn't really a job for a simple translation engine.

Whats the correct key/value Syntax to convert this Array with Symfony/XmlEncoder?

im building my request data as an array structure and want to use the symfony XmlEncoder to encode my Array to XML.
so i guess i got the fundamental part right, it looks like that for example:
$request_object = [
"acc-id" => $all_credentials,
"req-id" => $request_guid,
"tran-type" => "spec-url"
];
the syntax im looking for encodes in the following format, with attribute and value:
<amount currency="EUR">1.99</amount>
i have the possiblity to use the # sign on an array key, but how to also fit in the value?
$request_object = [
"acc-id" => $all_credentials,
"req-id" => $request_guid,
"tran-type" => "spec-url"
"am" => ["#attr"=>"attrval"]
];
this should be
<am attr="attrval"/>
but how to write it that i can also set the value? like:
<am attr="attrval">VALUE</am>
help is much appreciated
Use '#' as the index for the scalar value.
I found it by looking through the tests for the encoder.
#src:https://github.com/symfony/serializer/blob/master/Tests/Encoder/XmlEncoderTest.php
#line: 196
public function testEncodeScalarRootAttributes()
{
$array = [
'#' => 'Paul',
'#eye-color' => 'brown',
];
$expected = '<?xml version="1.0"?>'."\n".
'<response eye-color="brown">Paul</response>'."\n";
$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
}
...
#line: 234
public function testEncodeScalarWithAttribute()
{
$array = [
'person' => ['#eye-color' => 'brown', '#' => 'Peter'],
];
$expected = '<?xml version="1.0"?>'."\n".
'<response><person eye-color="brown">Peter</person></response>'."\n";
$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
}

Cakephp 3: Collections getting to middle array entities

I'm trying to build a view that shows the most recent (now four but eventually five) years of data in a chart. I'm able to pull the data successfully in a variable the shows the most recent 4 years(I don't have the fifth year in the database yet). In order to break it down for the view and add some calculations on associated arrays, I have created a collection. I can get to the first and last array entity but can not figure out how to break out the middle two arrays.
public function viewAsuFiveYear()
{
$asuFiveYearEnrollments = $this->Enrollments->find('all', [
'contain' => ['Azinstitutions', 'FallFtes', 'FallHeadcounts', 'ProjectedEnrollments',
'FallHeadcounts.Locations', 'FallHeadcounts.StudentTypes', 'FallHeadcounts.ResidentStatuses', 'FallHeadcounts.Campuses',
'FallFtes.Locations', 'FallFtes.StudentTypes', 'FallFtes.ResidentStatuses', 'FallFtes.Campuses']
], ['limit' => 5])->where(['azinstitution_id' => 1])->order(['enrollment_year' => 'DESC']);
$collection = new Collection($asuFiveYearEnrollments);
$yearone = $collection->first();
$yearTwo = $collection->take(1, 1);
$yearFour = $collection->last();
$collection1HC = new Collection($yearone->fall_headcounts);
$onCampusesHc1 = $collection1HC->match(['location_id' => 1 ])->match(['campus_id' => NULL]);
$collection4HC = new Collection($yearFour->fall_headcounts);
$onCampusesHc4 = $collection4HC->match(['location_id' => 1 ])->match(['campus_id' => NULL]);
$this->set(compact('asuFiveYearEnrollments', 'azinstitutions', 'yearone', 'yearTwo', 'yearFour', 'onCampusesHc1', 'onCampusesHc4'));
}
I tried ->take (one array, second position) but it times out with that. Not sure what filter I can use to get to the 2nd or 3rd entity array.
I believe I've figured it out. May not be the best answer but it works. for the second year I skipped the 1st array and grabbed the first array in what was left.
and the same with the third year.
$yearTwo = $collection->skip(1)->first();
$yearThree = $collection->skip(2)->first();

server timeout on Dynamo DB query operation to get items from a table

I have an array of ids (eventids) which can be million or billion ids inside the table. I am using a for loop to go through each id and retrieve an item from a dynamo db table using the query operation in PHP. However, because the query operation goes over a large amount of info in my table, it takes too long the query operation to do its work. So, in this case I don't get any result because I get a time out. The following code tries to do this operation, which is failing for large amounts of info. I am wondering if you have any recommendation or suggestions on how to make the query operation faster and doable for large amounts of ids inside the $sfweventarrayRDS array?
for ($j = 0 ; $j < count($sfweventarrayRDS) ; $j++){
$keyconditions = array(
"eventid" => array(
"ComparisonOperator" => ComparisonOperator::EQ,
"AttributeValueList" => array(
array(Type::NUMBER => $sfweventarrayRDS[$j]["eventid"])
)
)
);
$sfweventarrayDynamo = Dynamo::getItems("eventlocation",$keyconditions,$limit);
if (count($sfweventarrayDynamo) > 0){
$timediffepoch = $sfweventarrayDynamo[0]["edatecreated"]["N"] - $sfweventarrayRDS[$j]["edatecreated"];
$timediffstandard = new DateTime("#$timediffepoch");
if ($timediffstandard->format('i') >= 5){
$starttimeepoch = $sfweventarrayRDS[$j]["edatecreated"];
$endtimeepoch = $sfweventarrayDynamo[0]["edatecreated"]["N"];
$starttimestandard = new DateTime("#$starttimeepoch");
$endtimestandard = new DateTime("#$endtimeepoch");
$each_event = array("eventid" => $sfweventarrayDynamo[0]["eventid"]["N"],
"organizationid" => $sfweventarrayRDS[$j]["organizationid"],
"userid" => $sfweventarrayDynamo[0]["userid"]["N"],
"starttime" => $starttimestandard->format('Y-m-d H:i:s'),
"endtime" => $endtimestandard->format('Y-m-d H:i:s'),
"location" => $sfweventarrayRDS[$j]["location"],
"startlatitude" => $sfweventarrayRDS[$j]["latitude"],
"startlongitude" => $sfweventarrayRDS[$j]["longitude"],
"endlatitude" => $sfweventarrayDynamo[0]["latitude"]["N"],
"endlongitude" => $sfweventarrayDynamo[0]["longitude"]["N"]);
array_push($safewalkeventsDynamo, $each_event);
}
}
}
There can be two possible solutions that can improve your query operation:
Use batch get: If you already know event IDs and want to query the db to get more information, then batch get would be ideal. You create a chunk of 100 IDs (max allowed) and call DynamoDB to get information in one go. Repeat until you get all the IDs. Here is the documentation link.
You can increase your tables' read capacity according to your needs. (This will result in paying more money for dynamoDB.)

Unexpected notice: undefined index when null

I've written a little script that is supposed to create an array that looks something like this:
array(1 => array( 'month' => 'Jan',
'salary' => '01/31/2013',
'bonus' => '02/15/2013'),
2 => array('month' => '',...));
You get the basic idea: the index in the main array are the months (numerically), each holding an array that is to be filled dynamically. The month key depends on the language the user requested, the salary and bonus are assigned the dates when the salary and or bonus was payed. So far no surprizes.
To get the basic structure of that array, I thought this would be the easiest:
$this->months = array_fill_keys(range(1,12), array( 'month' => null,
'salary' => null,
'bonus' => null));
I then fill the array, and things run smoothly all the same, right up until the point where I want to write the data to a file, which I did like so:
private function writeFile()
{
foreach($this->months as $key => $vals)
{
if ($vals['month'] === null)
{//Only filled from date x to date y, some months can be empty
continue;
}
//this seems to raise notices?
if ($vals['salary'] === null)
{
$vals['salary'] = 'Does not apply';
}
fwrite($this->file, implode(',', $vals).PHP_EOL);
}
fclose($this->file);
return $this;
}
The line where I check if the salary is null raises notices: "Warning: Undefined index salary". At the moment I've had to add this to the code:
if (!array_key_exists('salary', $vals) || $vals['salary'] === null)
{
if (!array_key_exists('bonus', $vals) || $vals['bonus'] === null)
{
break;
}
$vals['salary'] = 'Does not apply';
}
To get to the result I need. I have googled this, and stumbled across this bug report, which was last modified 4 years ago (2009-05-08), but the status is still set to "no feedback".
Has anyone else encountered a similar glitch/bug? Or am I missing something here? How can I avoid this issue without the need for all too many if's and function calls without changing my settings (E_STRICT | E_ALL, as it should be).
BTW: I'm running PHP 5.4.7, on Slackware 14. For this little application, I'm using 2 Symfony components (ClassLoader and Console), but since this is part of an object that has nothing to do with Symfony, apart from being loaded through UniversalClassLoader I don't think that's relevant.
Since the bug is said to be PDO related: yes, I am using PDO, but in another class.
I am not sure , but try to use
$this->months = array_fill(1,12, array( 'month' => null,
'salary' => null,
'bonus' => null));
After a couple of var_dumps, I found out what the cause was: The array keys were range(1,12), to determine the which month was I was processing. For that I used a DateTime object in the following fashion:
$date->modify('+ '.$diff.' days');
$key = $date->format('m');
The problem was that the format call returns a string. The goal was to list when the salary and bonus are to be paid. The bonus has to be paid each 15th, or the following Wednesday, if the 15th was a Saturday or Sunday. The salary is to be paid the last day of the month, or the last Friday.
In other words, the bonus pay date is assigned like so:
$key = $date->format('m');
$this->months[$key]['month'] = $date->format('M');
if ($date->format('d') == 15)
{
//find find week-day (15th or following Wednesday)
$this->months[--$key]['bonus'] = $date->format('m/d/Y');
$key++;
//set date to end of month
}
//check week-day, and modify date if required
$this->months[$key]['salary'] = $date->format('m/d/Y');
Because the keys of the $this->months array are numeric, but the format used for $key is a 2-digit string, with leading zeroes, I was having problems.
Each 15th of the month, the $key value is coerced to an integer (decrement/increment operators), but the month is assigned using a string.
The information I provided in my original question was insufficient, sorry for that, but I had just put in an all-nighter. The fix, in the end was pretty easy:
$key = (int) $date->format('m');//cast
I sincerely appreciate all responses, and everyone contributing to the SO community. I would've deleted this question, but if nobody objects, I think I might leave it be as a testimony to my stupidity.

Resources