How to use Yii CArrayDataProvider - yii

I am new to php and Yii and need some help regarding showing array in the webpage.
In the controller I open my e-mail inbox and iterate through the e-mails in inbox and build array with each e-mail address as key having values
if (array_key_exists($fromemail,$senders))
{ $senders[$fromemail]['rcount']++; }
else {
$senders[$fromemail]['e-mail'] = $fromemail;
$senders[$fromemail]['Name'] = $fromname;
$senders[$fromemail]['rcount'] = 1;
}
$model->top_senders = $senders;
$this->render('Step2',array('model'=>$model,));
Then in the view file of Step2 I want to show the data in CGridview
if (isset($model->top_senders))
{
$gridDataProvider = new CArrayDataProvider($model->top_senders);
$gridDataProvider->setData($model->top_senders);
$gridColumns = array(
array('name'=>'e-mail', 'header'=>'E-mail','value' =>'$data->e-mail'),
array('name'=>'rcount', 'header'=>'# of mails','value'=>'$data->rcount'),);
$this->widget('bootstrap.widgets.TbGridView',array('dataProvider' => $gridDataProvider,'template' => "{items}",'columns'=>$gridColumns));
}
But I will get error during rendering of the table: PHP notice Undefined offset: 0
/**
125 * Renders a data cell.
126 * #param integer $row the row number (zero-based)
127 */
128 public function renderDataCell($row)
129 {
130 $data=$this->grid->dataProvider->data[$row];
What I am doing wrong? Can anyone help me?

You should't add data to provider as follows:
$gridDataProvider->setData($model->top_senders);
It added during initialization. You must be sure that the array has a key id, otherwise you need to specify it manually (it must be unique) as follows:
$gridDataProvider = new CArrayDataProvider($model->top_senders, array(
'id'=>'Name',
));
You will also need to make sure that the array $model->top_senders has the following structure:
array(
'0'=>array(...user data here),
'1'=>array(...user data here),
...
);

If you var_dump($gridDataProvider->data) you'd notice there's no value for the 0th index in the array. This happens when you run some filter function on the array. Assuming there are 5 values in the filtered array, the filtered array would look something like this when var_dump'd.
array(5) {
[1] => Object (Mailer) {…},
[2] => Object (Mailer) {…},
[4] => Object (Mailer) {…},
[8] => Object (Mailer) {…},
[9] => Object (Mailer) {…},
}
A filter operation on an array can leave the array looking like the above.
When the CGridView is trying to feed the view with data, it does it sequentially—this, I find, is a shortcoming in Yii and should be raised as an issue.
In order to fix this, use PHP's array_values() like so…
$properly_indexed_array = array_values($filtered_array);
This will copy the values of the filtered array into a new array. This is not the optimal solution in terms of memory. So far, I do not see any means in PHP other than this though.
You may then go ahead and set this as the data for your data provider like so…
$gridDataProvider->setData($properly_indexed_array);

Related

How to extract a specific element value from an array in laravel controller

I do have the following output in the form of an array
$fetchSubFormDetailData = $this->PMSDS11FetchSubFormDetailTrait($request);
echo 'Data Submitted' ;
print_r($fetchSubFormDetailData);
The result shown because of print_r is as under
Data SubmittedArray
(
[LineId] => 10
[IncomeTo] => 500000.00
)
Now I want to extract IncomeTo from this array into a variable in this controller for validations. I tried using the following ways
$IncomeTo = $fetchSubFormDetailData[0]->IncomeTo;
I am keep on getting the following error in laravel8.
"message": "Undefined array key 0",
"exception": "ErrorException",
How can I extract the value (500000.00) of IncomeTo element from this array in my $IncomeTo variable?
Any help will be appreciated.
This should work
$IncomeTo = $fetchSubFormDetailData['IncomeTo'];

How to delete items from an array in Vue

I have a function called updateAnswer with multiple dynamic parameters.
updateAnswer(key, answer, array = false) {
if (array) {
if(this.answers.contains.find(element => element === answer)) {
//Delete item from array if already element already exists in this.answers.contains array.
} else {
Vue.set(this.answers, key, [...this.answers.contains, answer]);
}
} else {
Vue.set(this.answers, key, answer);
}
},
I'd like to know how delete an item in the array if the value already exists in the array.
You can use method called splice:
Just reference on your array and set values in the brackets the first is referenced on the position, the second is how many datas you want to splice/delete.
The function looks like this:
this.array.splice(value, value)
Lets see on an example - you have array food= [apple, banana, strawberry] than I'm using this.food.splice(1,1)..
my array looks now like this food = [apple, strawberry] - first value in my brackets are the position, the second one is the amount of "numbers" you want to delete.
Hopefully this helps you out!
I suppose each value in this.answers.contains is unique?
Anyways, if you just want to delete the item if already exists, I suggest filter(). It should look like below:
if(this.answers.contains.find(element => element === answer)) {
this.answers.contains = this.answers.contains.filter(c => c !== answer)
}
Also, the if condition if(this.answers.contains.find(element => element === answer)) could also be replaced by if(this.answers.contains.includes(answer))
Hope that could help you.

Am I overwriting computed property filter in Vue?

I am trying to create a reactive filter for an array in Vue. My starting array comes from an API call which returns this.features (geojson features). I am filtering on a nested array. This works -- but when I enter a search term and then backspace back out to an empty string, and enter another string, I am not filtering the original array but appear to be filtering the already-filtered array. How could I filter again on the original array from the API call?
computed property:
filteredFeatures() {
if (this.searchTerm == '') {
return this.features
}
// filter on nested array
let filtered = this.features.filter(feature => {
feature.properties.site_observations = feature.properties.site_observations.filter(
el => JSON.stringify(el).match(this.searchTerm, 'i')
)
return feature.properties.site_observations.length > 0
})
return filtered
}
I have looked at Vue filtering objects property but I cannot make that code work (it uses Object.assign()). Thanks for any ideas.
Your computed property is mutating feature.properties.site_observations, that's a nono. Computed properties should be read only.
filteredFeatures() {
if (this.searchTerm == '') {
return this.features
}
// filter on nested array
let filtered = this.features.filter(feature => {
const site_observations = feature.properties.site_observations.filter(
el => JSON.stringify(el).match(this.searchTerm, 'i')
)
return site_observations.length > 0
})
return filtered
}
It seems here is your problem:
feature.properties.site_observations = feature.properties.site_observations.filter(
el => JSON.stringify(el).match(this.searchTerm, 'i')
)
Because this code filter feature and alter the proprieties of feature.properties.site_observations. Then, in the next read the value is alter. We say that your function it is not pure, because it alter the state of feature.
So, what you should do is:
let anotherVariable = feature.properties.site_observations.filter(
el => JSON.stringify(el).match(this.searchTerm, 'i')
)
Therefore, on a function, avoid alter state of objects, this lead to bugs.
On further checking, the above answer returns all site_observations, not just the ones that match the search. A much better solution is the following, using map to avoid overwriting the data, and the object spread operator to perform an object assign, and drilling down through the nested objects as follows:
filteredFeatures() {
return this.features
.map(feature => ({
...feature,
properties: {
site_observations: feature.properties.site_observations.filter(
element => {
return JSON.stringify(element).match(new RegExp(this.search, 'i'))
}
)
}
}))
.filter(feature => feature.properties.site_observations.length)
}

PayPal Api Patch on Itemlist doesn't work

I want to add an item to my transaction.
$json = '
[
{
"name": "Voucher",
"description":"Voucher",
"price":"50.00",
"currency":"EUR",
"quantity":"1"
}
]';
$patchAddItem = new \PayPal\Api\Patch();
$patchAddItem->setOp('add')
->setPath('/transactions/0/item_list/items')
->setValue(json_decode($json));
$patchReplace = new \PayPal\Api\Patch();
$patchReplace->setOp('replace')
->setPath('/transactions/0/amount')
->setValue(json_decode('{
"total": "159.00",
"currency": "EUR",
}'));
$patchRequest = new \PayPal\Api\PatchRequest();
$patchRequest->setPatches(array($patchAddItem, $patchReplace));
try {
$this->payment->update($patchRequest, $this->apiContext);
} catch (PayPal\Exception\PayPalConnectionExceptio $ex) {
echo '<pre>';print_r(json_decode($ex->getData()));exit;
}
But I get following Error
Eception: Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/payments/payment/PAY... in PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Core/PayPalHttpConnection.php on line 154
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Transport/PayPalRestCall.php on line 73: PayPal\Core\PayPalHttpConnection->execute("[{"op":"add","path":"/transactions/0/item_list/ite"... )
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Common/PayPalResourceModel.php on line 102: PayPal\Transport\PayPalRestCall->execute(array[1],"/v1/payments/payment/PAY-1S151200BX2478240LEAG3CI","PATCH","[{"op":"add","path":"/transactions/0/item_list/ite"... ,null)
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Api/Payment.php on line 615: PayPal\Common\PayPalResourceModel::executeCall("/v1/payments/payment/PAY-1S151200BX2478240LEAG3CI","PATCH","[{"op":"add","path":"/transactions/0/item_list/ite"... ,null,object,null)
At this moment I didn't execute the payment object. Do I have to edit the total attribut from amount too? Well, I tried this too, with same issue...
Even if you are sending only one item to PayPal you still have to set them as an item list with setItemList().
That array should be visible if you json_decode in your payment array:
[item_list] => Array
(
[items] => Array
(
[0] => Array
(
[name] => Ground Coffee 40 oz
[sku] => 123123
[price] => 52.80
[currency] => USD
[quantity] => 1
)
)
I had not run a patch for an item yet. I attempted to send an 'add' similar to your code and tried changing the path to '/transactions/0/item_list/items/1' using the next number in the items array. But could not get an add to work.
The only way I could modify the item_list was to do a complete 'replace' of the item_list, so in a running shopping cart would have to include all the items being purchased, not just the new item.
To do this I prefer to use the functions from the PayPal sdk vs building the json arrays. Their examples of how to create and execute a payment are fairly good and use the SDK functions. http://paypal.github.io/PayPal-PHP-SDK/sample/
However the example on updating a payments builds the json arrays outright.
Below is a testing function to modify the item_list using the Paypay PHP SDK Class Functions. I hard coded the Subtotal and Total to match the values coming form the shopping cart plus the increase from the new item. The item_list is also hard coded using PP's example data. Otherwise item's arrays would be built off of a user's shopping cart items. The type is set to 'replace'.
So, yes. Subtotals and Totals need to be updated to match as well, else the PP call will fail.
function updatePayPalPayment ($type, $createdPayment, $total, $subtotal, $shipping, $currency) {
$subtotal = '54.80';
$total = '71.73';
$details = new Details();
$details->setShipping($shipping)
->setSubtotal($subtotal);
$amount = new Amount();
$amount->setCurrency($currency)
->setTotal($total)
->setDetails($details);
$item1 = new Item();
$item1->setName('Ground Coffee 40 oz')
->setCurrency('USD')
->setQuantity(1)
->setSku("123123") // Similar to `item_number` in Classic API
->setPrice(52.80);
$item2 = new Item();
$item2->setName('Granola bars')
->setCurrency('USD')
->setQuantity(1)
->setSku("321321") // Similar to `item_number` in Classic API
->setPrice(2.0);
$itemList = new ItemList();
$itemList->setItems(array($item1, $item2));
$patchItem = new Patch();
$patchItem->setOp($type)
->setPath('/transactions/0/item_list')
->setValue($itemList);
$patchAmount = new Patch();
$patchAmount->setOp($type)
->setPath('/transactions/0/amount')
->setValue($amount);
$patchRequest = new PatchRequest();
$patchRequest->setPatches(array($patchAmount, $patchItem));
$update = $createdPayment->update($patchRequest, getApiContext());
return $update;
}
I also have found it very helpful to set the apiContext for logging to DEBUG and output to a file in development for much better error messages.
'log.LogEnabled' => true,
'log.FileName' => '_PayPal.log',
'log.LogLevel' => 'DEBUG',
Hope that helps.

Yii Pagination Variables from DataProvider

I need certain pagination variables in my controller action.
such as:
1.Current Page Number
2.Current Page offset
3.total records displayed
i.e. 31 to 40 of 2005 records displayed
I tried the following:
$dataProvider = NodesTerms::getNodesDataFromTerms($nodeId) ;
$pagination = $dataProvider->getPagination();
var_dump($pagination->getPageCount());
//var_dump($pagination->currentPage);
I can get the Pagination Object, but zero (0) in $pagination->currentPage or $pagination->offset etc....
I need this information to dynamically generate meta page title and description on actions with page listings such as pagetitle: page 3 of 10 for American Recipes...
Appreciate any help with this.
Try setting the itemCount explicitly in your dataProvider:
'pagination'=>array(
'pageSize'=>10,
'itemCount'=>$count
)
Or use a new CPagination object:
$pagination = new CPagination($count);
$dataProvider = new CSqlDataProvider($sql, array(
// ... other config
'pagination' => $pagination
));
How this works:
The pagination's itemCount is set during creation of the data-provider and again in CSqlDataProvider's fetchData function:
$pagination->setItemCount($this->getTotalItemCount());
During creation of data-provider only the values passed to the pagination property are used, and if we don't pass itemCount value, then the default of 0 is used.
So if we want to access offset or pageCount or currentPage or itemCount before the call to fetchData we have to set the itemCount explicitly.
However if we want those values after the call to fetchData then the values are already correctly populated due to the call to setItemCount within fetchData.
An example for clarity (without passing itemCount during data-provider creation):
$dataProvider = NodesTerms::getNodesDataFromTerms($nodeId);
$pagination = $dataProvider->getPagination();
var_dump($pagination->getPageCount()); // this will be zero
$data=$dataProvider->getData(); // getData calls fetchData()
var_dump($pagination->getPageCount()); // now this will be correct value
getCurrentPage returns "the zero-based index of the current page"
http://www.yiiframework.com/doc/api/1.1/CPagination#getCurrentPage-detail
so if you're on the first page, it should be returning 0.
And as you know the page size and the total number of records that will be enough for you to generate your page title.