Problem with Collection::sortBy() in cakephp 4 - sorting

I'm trying to sort a list of email accounts by alphabetical order using Collection::sortBy() in cakephp 4, but it seems not working the way I use it.
$accounts = [
[
'email' => 'webmaster#example.com',
'isBlocked' => false,
],
[
'email' => 'dom#example.com',
'isBlocked' => false,
],
[
'email' => 'me#example.com',
'isBlocked' => false,
],
[
'email' => 'guy#example.com',
'isBlocked' => false,
],
[
'email' => 'test#example.com',
'isBlocked' => false,
]
];
$sorted = collection($accounts)
->sortBy('email', SORT_ASC)
->toArray();
debug($sorted);
debug($sorted) returns exactly the same array as $accounts...
What am I doing wrong ?

The default sort type is SORT_NUMERIC, and converted to numbers, all your strings will be 0, hence all are equal as seen by the sorting mechanism, and nothing will chance.
For strings use SORT_NATURAL, SORT_STRING, SORT_LOCALE_STRING, or SORT_REGULAR, eg:
sortBy('email', SORT_ASC, SORT_NATURAL)
The Cookbook needs a fix there I think, as it shows referencing string fields without specifying the required sort type.
See also
PHP Manual > Function Reference > Variable and Type Related Extensions > Arrays > Array Functions > sort
Cookbook > Collections > Sorting

Related

How to assert array value but not strict?

How can we assert some of the array's property values which contain the expected object values?
My code below is working okay, but it checks all array property values. I want to ask if there's a way we can check only some of it.
$dataToBeTested = [
'name' => 'Johnny',
'address' => 'Somewhere',
'age' => 21,
'card_no' => 13331577121,
'rep_no' => 441546661,
'status' => 'in-progress',
'created_at' => '2022-07-31T10:05:27.011000Z',
'updated_at' => '2022-07-31T10:05:27.011000Z',
];
$expectedPropValue = [
'name' => 'Johnny',
'address' => 'Somewhere',
'age' => 21,
];
as expected it will return fail, since expectedPropValue has some missing properties.
$this->assertEquals($dataToBeTested, $expectedPropValue);
Goal is something like this,
$this->assertSomeOfIt($dataToBeTested, $expectedPropValue); // return true
You are looking for assertArraySubset, Which it's been deprecated with the newer version of PHPUnit. As mentioned here https://github.com/sebastianbergmann/phpunit/issues/3494. But you can always introduce your approach and use it for your project. Or you can also try this package https://packagist.org/packages/dms/phpunit-arraysubset-asserts. which gives you what you need
self::assertArraySubset($expectedSubset, $content, true);

Laravel: How can I assertJson an array

I am creating a feature test for a Seminar. Everything is working great; I am trying to update my feature test to account for the seminar dates.
Each Seminar can have one or many dates, so I am saving these values as a json field:
// migration:
...
$table->json('dates');
...
Here is what my Seminar model looks like:
// Seminar.php
protected $casts = [
'dates' => 'array',
];
When saving the seminar, I am returning a json resource:
if ($seminar->save()) {
return response()->json(new SeminarResource($seminar), 200);
...
Using Postman, my Seminar looks a like this:
...
"capacity": 100,
"dates": [
"2020-10-15",
"2020-10-16"
],
...
So far so good!
In my test, I am testing that a seminar can be created.
$http->assertStatus(201)
->assertJson([
'type' => 'seminars',
'id' => (string)$response->id,
'attributes' => [
'dates' => $response->attributes->dates, // General error: 25 column index out of range
I've tried to convert the array to a string, or json_encode the value in the resource. I don't think that's the correct way since I am already casting the value as an array in the model.
How can I assert that my dates is returning an array?
+"dates": array:2 [
0 => "2020-10-15"
1 => "2020-10-16"
]
Thank you for your suggestions!
EDIT
When I dd($response->attributes->dates); this is what I'm getting (which is correct).
array:2 [
0 => "2020-10-15"
1 => "2020-10-16"
]
What I'm not sure is how to assert an array like that. Since I'm using faker to generate the date, I don't really know (or care) what the date is, just want to assert that it is in fact an array.
I've tried something like:
'dates' => ['*'],
However, that just adds another element to the array.
EDIT 2
If I make the array a string,
'dates' => json_encode($response->attributes->dates),
I'll get an error like this:
--- Expected
+++ Actual
## ##
- 'dates' => '["2020-10-15","2020-10-16"]',
+ 'dates' =>
+ array (
+ 0 => '2020-10-15',
+ 1 => '2020-10-16',
+ ),
In my database, the values are stored like this:
["2020-10-15","2020-10-16"]
My actual test looks like this:
$http->assertStatus(201)
->assertJsonStructure([
'type', 'id', 'attributes' => [
'name', 'venue', 'dates', 'description', 'created_at', 'updated_at',
],
])
->assertJson([
'type' => 'workshops',
'id' => (string)$response->id,
'attributes' => [
'name' => $response->attributes->name,
'venue' => $response->attributes->venue,
'dates' => $response->attributes->dates,
'description' => $response->attributes->description,
'created_at' => (string)$response->attributes->created_at,
'updated_at' => (string)$response->attributes->updated_at,
],
]);
$this->assertDatabaseHas('workshops', [
'id' => $response->id,
'name' => $response->attributes->name,
'venue' => $response->attributes->venue,
'dates' => $response->attributes->dates,
'description' => $response->attributes->description,
]);

YII2: custom sorting in search model

Please, help me with such a problem:
1) I have default search model of Users.
2) I need a list of users. And first in this list always must be user with login 'admin', and second - with login 'finance', and then all others sorted by id.
My method in UserController
public function actionUsersList() {
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->renderPartial('users-list', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
As I understood I have to change params of search in this line, to add sort conditions
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
But how exactly can I do this?
You can do that by adding following code to your action:
$dataProvider->sort->attributes['id'] = [
'asc' => [
new \yii\db\Expression("FIELD(login, 'finance', 'admin') DESC"),
'id' => SORT_ASC,
],
'desc' => [
new \yii\db\Expression("FIELD(login, 'finance', 'admin') DESC"),
'id' => SORT_DESC,
],
'label' => $searchModel->getAttributeLabel('id'),
];
$dataProvider->sort->defaultOrder = ['id' => SORT_ASC];
The field function returns the position of first parameter among other parameters or 0 if the value is not present among them. So for 'admin' it will return 2, for 'finance' 1 and for others 0. If you order DESC by that you will get the required order.
Other option is to add this definitions for sort into the search method of UserSearch model as suggested in mahsaa's answer. It depenends if you want to use this sorting in different actions.
In UserSearch class, add sort to ActiveDataProvider:
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => [
'login' => SORT_ASC,
'id' => SORT_DESC,
],
]]);
It first sorts by login and then id.

How to enable and disable sort in Yii2 GridView?

How to enable and disable sort in Yii2 GridView ?
You can customize columns sort in your DataProvider. For example if you use ActiveDataProvider in your GridView you can indicate sort-able columns like below:
$dataProvider = new ActiveDataProvider([
'query' => Model::find(),
'sort' => ['attributes' => ['column1','column2']]
]);
In above example, only column1 and column2 are sort-able.
You can also disable sorting for all columns like below:
'sort' =>false
It is suggested to take a look at Yii2's official document : Class yii\data\Sort As it defines it:
Sort represents information relevant to sorting.
When data needs to be sorted according to one or several attributes, we can use Sort to represent the sorting information and generate appropriate hyperlinks that can lead to sort actions.
In addition to Ali's answer, for aggregated and related columns you could do the following:
public function actionIndex()
{
$dataProvider = new ActiveDataProvider([
'query' => User::find()->joinWith('role'),
'sort' => ['attributes' => [
//Normal columns
'username',
'email',
//aggregated columns
'full_name' => [
'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
'default' => SORT_DESC
],
//related columns
'role.name' => [
'asc' => ['user_role.name' => SORT_ASC],
'desc' => ['user_role.name' => SORT_DESC],
'default' => SORT_DESC
],
],],
]);
}
Source: http://www.yiiframework.com/doc-2.0/yii-data-sort.html
If you want disable sorting from gridview for particular column then do like this:
[
'attribute' => 'name',
'enableSorting' => false
],
by using 'enableSorting' => false
You can disable sort in controller like this:
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->sort->sortParam = false;

sending form data via PHP

I'm trying to submit a form using PHP and the mailchimp 2.0 api.
I'm getting an error that says:
FNAME must be provided
My form has a field for the first name:
<input type="text" name="fname">
So it must have something to do with the way I am handling it the php side.
Here is the bit of php that handles FNAME:
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'myid',
'email' => array( 'email' => $_POST['email']),
'FNAME' => $_POST['fname'],
'LNAME' => $_POST['lname'],
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false
));
I'm not sure if I'm forming the array correctly or not.
By the way, I'm using this wrapper, but I think my error has to do with how I create $result and not the wrapper.
https://github.com/drewm/mailchimp-api
Any help would be appreciated.
Thanks!
Peep the example at the bottom of the page you linked to. I've pasted it here:
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'b1234346',
'email' => array('email'=>'davy#example.com'),
'merge_vars' => array('FNAME'=>'Davy', 'LNAME'=>'Jones'),
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false,
'send_welcome' => false,
));
print_r($result);
Your merge vars (ex. FNAME and LNAME) need to be in its own array. So, add a 'merge_vars' in your array and create an array that contains your field's merge variables.

Resources