Yii2 add related attribute to sort - sorting

I have normal ModelSearch with ActiveDataProvider, and I would like to add a virtual/related attribute to sorting in gridview. If I'm doing with setSort, and I'm adding this only attribute, then all other attributes are not sortable any more. Is there a built-in way to add an attribute to Sort? Thanks a lot!
public function search($params) {
$query = Za::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => ['aonr' => SORT_ASC]],
'pagination' => [
'pageSize' => 15,
],
]);
$dataProvider->setSort([
'attributes' => [
'lwnr' => [
'asc' => ['lwnr' => SORT_ASC],
'desc' => ['lwnr' => SORT_DESC],
'label' => 'lwnr',
'default' => SORT_DESC,
],
]
]);
$this->load($params);
...
}

You can set the sortable attributes with the setSort method, but in this case you need to set all the columns you want to sort, not just the column from the relation.
If you want to add one column you can try this (merging the currently existing sort attributes with the new one):
$dataProvider->setSort([
'attributes' => array_merge(
$dataProvider->getSort()->attributes,
[
'lwnr' => [
'asc' => ['lwnr' => SORT_ASC],
'desc' => ['lwnr' => SORT_DESC],
'label' => 'lwnr',
'default' => SORT_DESC,
],
]
),
]);
or you can add the missing attributes/columns by hand (which is a far better idea)
$dataProvider->setSort([
'attributes' =>
[
'lwnr' => [
'asc' => ['lwnr' => SORT_ASC],
'desc' => ['lwnr' => SORT_DESC],
'label' => 'lwnr',
'default' => SORT_DESC,
],
// Other attribute
'id' => [
'asc' => ['id' => SORT_ASC],
'desc' => ['id' => SORT_DESC],
],
...
],
]);
Another way:
$dataProvider->getSort()->attributes['lwnr'] = [
'asc' => ['lwnr' => SORT_ASC],
'desc' => ['lwnr' => SORT_DESC],
'label' => 'lwnr',
'default' => SORT_DESC,
];

Related

I have a problem with repeatable -> select2_from_array field in BackPack

I have this code:
Controller
$this->crud->addField(
[
'name' => 'schedule',
'label' => 'Schedule',
'type' => 'repeatable',
'fields' => [
[
'name' => 'day',
'label' => 'Day',
'type' => 'select_from_array',
'options' => Day::titles(),
'allows_null' => false,
],
[
'name' => 'range',
'label' => 'Range',
'type' => 'select2_from_array',
'options' => $this->getScheduleRange(),
'default' => $this->getDefaultScheduleRange(),
'allows_null' => false,
"allows_multiple" => true,
],
],
]
);
Model
protected $casts = [
'schedule' => 'array',
];
stored data in DB(schedule column):
[{"day": "1", "range[]": ["1:30:00", "2:00:00"]}]
But selected data not showing on the page when it is multi selected.
UPD:
After Pedro's recomandation it's not help me. In DB it's storing as:
[{"day": "1", "range[]": ["0", "1"]}]
viper.
when using multiple with default please use numeric keys like:
'options' => [0 => 'option 0', 1 => 'option 1'],
'default' => [0,1]
I think Backpack can do something here to improve multiple string keys like
'options' => ['option_0', 'option_1'],
'default' => ['option_0', 'option_1']
I will open an issue to discuss this in the package repository.
Thanks
Pedro

Elasticsearch mapping with nested objects

It is my mapping. product_base is nested object also manufacturer is child of product_base and nested object. When i create my doc, if product_base count more than 1 , seems product_base.0, product_base.1 . However i couldnt use filter normally. Is my mapping wrong?
When i use filter for example , returns null because in my elasticserach doc. product_base seems as product_base.0 , product_base.1 ....
'path' => 'product_base.lang',
'query' => [
'bool' => [
'must' => [
['match' => [
product_base.lang.id_product' => 1]],
['match' => [
'product_base.lang.id_lang' => 2]],
],
],
$params = 'index' => product,
'body' => [
'mappings' => [
'_source' => [
'enabled' => true,
],
'properties' => [
'id_product' => ['type' => 'integer'],
'id_product_base' => ['type' => 'integer'],
'lang' => [
'type' => 'nested',
'properties' => [
'id_product' => ['type' => 'integer'],
'id_lang' => ['type' => 'byte'],
'name' => ['type' => 'text']
],
],
'product_base' => [
'type' => 'nested',
'properties' => [
'id_product_base' => ['type' => 'integer'],
'id_manufacturer' => ['type' => 'integer'],
'id_product_zone_group' => ['type' => 'text'],
'status' => ['type' => 'byte'],
'manufacturer' => [
'type' => 'nested',
'properties' => [
'id_manufacturer' => ['type' => 'integer'],
'name' => ['type' => 'text'],
'status' => ['type' => 'byte'],
],
],
]
]
]
]
]
]
];

Proper formatting of a JSON and multiplepart Guzzle post request

I have two different Guzzle post requests that I am trying to merge (solely because they basically do a united job and should be performed together).
Initially I have my donation data:
'donation' => [
'web_id' => $donation->web_id,
'amount' => $donation->amount,
'type' => $donation->type,
'date' => $donation->date->format('Y-m-d'),
'collection_id' => NULL,
'status_id' => $donation->status_id,
],
And then I have my files that go with it, which are basically two different PDFs that are enabled or disabled for donors, sometimes they have both. I know the multipart would look something like below, but I'm not sure.
foreach ($uploadDocs as $doc) {
'multipart' => [
[
'name' => 'donation_id',
'contents' => $donation->web_id,
],
[
'name' => 'type_id',
'contents' => $doc->type_id',
],
[
'name' => 'file',
'contents' => fopen($doc->path, 'r'),
'headers' => ['Content-Type' => 'application/pdf'],
],
],
}
Since I've usually only handled one file at a time and I'm not sure how to merge the first block of code with the second for an appropriate Guzzle post request.
You can try this:
$donationData = [
'web_id' => $donation->web_id,
'amount' => $donation->amount,
'type' => $donation->type,
'date' => $donation->date->format('Y-m-d'),
'collection_id' => NULL,
'status_id' => $donation->status_id,
];
$multipart = [];
foreach ($uploadDocs as $doc) {
$multipart[] = [
[
'name' => 'donation_id',
'contents' => $donation->web_id,
],
[
'name' => 'type_id',
'contents' => $doc->type_id,
],
[
'name' => 'file',
'contents' => fopen($doc->path, 'r'),
'headers' => ['Content-Type' => 'application/pdf'],
],
];
}
Than perform your request:
$r = $client->request('POST', 'http://example.com', [
'body' => $donationData,
'multipart' => $multipart,
]);

Ignore Results Outside of Distance Range

I am working with ElasticSearch for an application which deals with "posts". I currently have it working with a geo_point so that it will return all posts ordered by distance from the end-user. While this is working I also need to work in one more aspect for the system.
Posts can be paid for and for instance if I were to pay for my post and choose "Local" as the area range then this post should only show to end-users which are less than or equal to 20 miles away.
I have a column on my index named spotlight_range, is there a way I can create a query to say ignore all records if the spotlight_range = 'Local' and the distance is > 20 miles? I need to do this for several different spotlight ranges. For instance Regional may be 100 miles or less, etc.
My current query looks like this
$params = [
'index' => 'my_index',
'type' => 'posts',
'size' => 25,
'from' => 0,
'body' => [
'sort' => [
'_geo_distance' => [
'post_location' => [
'lat' => '44.4759',
'lon' => '-73.2121'
],
'order' => 'asc',
'unit' => 'mi'
]
],
'query' => [
'filtered' => [
'query' => [
'match_all' => []
],
'filter' => [
'geo_distance' => [
'distance' => '100mi',
'post_location' => [
'lat' => '44.4759',
'lon' => '-73.2121'
]
]
]
]
]
]
];
My index is setup with the following fields.
'id' => ['type' => 'integer'],
'title' => ['type' => 'string'],
'description' => ['type' => 'string'],
'price' => ['type' => 'integer'],
'shippable' => ['type' => 'boolean'],
'username' => ['type' => 'string'],
'post_location' => ['type' => 'geo_point'],
'post_location_string' => ['type' => 'string'],
'is_spotlight' => ['type' => 'boolean'],
'spotlight_range' => ['type' => 'string'],
'created_at' => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss'],
'updated_at' => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss']
My end goal for this is not specifically to search for distance < X and range = Y but rather to have it filter them out for all types based on distances I specify. The search should return ALL types of ranges but also filter out anything past my specified distance for each range type based on the users lat/lon passed into the query.
I have been looking for a solution to this online without much luck.
I would add a circle geo_shape to the document, centered on post_location and with a radius corresponding to the spotlight_range since you know both information at indexing time. That way you can encode into each post its corresponding "reach".
...
'post_location' => ['type' => 'geo_point'],
'spotlight_range' => ['type' => 'string'],
'reach' => ['type' => 'geo_shape'], <---- add this
So a "local" document would look something like this once indexed
{
"spotlight_range": "local",
"post_location": {
"lat": 42.1526,
"lon": -71.7378
},
"reach" : {
"type" : "circle",
"coordinates" : [-71.7378, 42.1526],
"radius" : "20mi"
}
}
Then the query would feature another geo_shape centered on the user's location with the chosen radius and would only retrieve documents whose reach intersects the circle shape in the query.
$params = [
'index' => 'my_index',
'type' => 'posts',
'size' => 25,
'from' => 0,
'body' => [
'sort' => [
'_geo_distance' => [
'post_location' => [
'lat' => '44.4759',
'lon' => '-73.2121'
],
'order' => 'asc',
'unit' => 'mi'
]
],
'query' => [
'filtered' => [
'query' => [
'match_all' => []
],
'filter' => [
'geo_shape' => [
'reach' => [
'relation' => 'INTERSECTS',
'shape' => [
'type' => 'circle',
'coordinates' => [-73.2121, 44.4759],
'radius' => '20mi'
]
]
]
]
]
]
]
];

How to always show single validation message in ZF2 validators?

I have the following input:
private function addBirthdayElement()
{
return $this->add(
array(
'type' => 'DateSelect',
'name' => 'x_bdate',
'options' => [
'label' => 'astropay_birthday',
'label_attributes' => array(
'class' => 'astropay-label'
),
'create_empty_option' => true,
'render_delimiters' => false,
],
'attributes' => array(
'required' => true,
'class' => 'astropay-input',
)
)
);
}
It has the following filter:
public function addBirthdayFilter()
{
$time = new \DateTime('now');
$eighteenYearAgo = $time->modify(sprintf('-%d year', self::EIGHTEEN_YEARS))->format('Y-m-d');
$this->add(
[
'name' => 'x_bdate',
'required' => true,
'validators' => [
[
'name' => 'Between',
'break_chain_on_failure' => true,
'options' => [
'min' => 1900,
'max' => $eighteenYearAgo,
'messages' => [
Between::NOT_BETWEEN => 'astropay_invalid_birth_date_18',
Between::NOT_BETWEEN_STRICT => 'astropay_invalid_birth_date_18',
]
]
],
[
'name' => 'Date',
'break_chain_on_failure' => true,
'options' => [
'messages' => [
Date::INVALID => 'astropay_invalid_birth_date',
Date::FALSEFORMAT => 'astropay_invalid_birth_date',
Date::INVALID_DATE => 'astropay_invalid_birth_date',
],
]
],
],
]
);
return $this;
}
However, putting an empty date, I get the error message defined for:
Date::INVALID_DATE
But it's not the overridden one. The break_chain_on_failure works for the two validators I have defined, but the default Zend message is always there. For example I get this as an error in my form:
The input does not appear to be a valid date
astropay_invalid_birth_date_18
How can I display only the overidden error messages and 1 at a time?
You can use a message key in your validator configuration instead of a messages array to always show a single message per validator.
For example, replace this:
'options' => [
'messages' => [
Date::INVALID => 'astropay_invalid_birth_date',
Date::FALSEFORMAT => 'astropay_invalid_birth_date',
Date::INVALID_DATE => 'astropay_invalid_birth_date',
],
]
with this one:
'options' => [
'message' => 'Invalid birth date given!',
]

Resources