Remove array element with YiiMongoDbSuite - php

I have some code that should be deleting a record from an embedded MongoDB document.
Here is the code:
public function actionDeleteSaved()
{
$savedLink = $_POST['savedLink'];
$userId = Yii::app()->user->getId();
$current = SaveLink::model()->findByPk($userId);
if(in_array($savedLink, $current->links))
{
array_slice($current->links, $savedLink);
$current->save();
}
}
This is what is passing the data to the controllers action method:
echo CHtml::ajaxButton(
'delete',
Yii::app()->createUrl("dashboard/index/deletesaved"),
array( // ajax options
'type' => 'POST',
'context' => "js:this",
'data' => array(
'savedLink' => $savedLink
)
),
array( //html options
'class'=>'deleteSaved'
)
);
This is what renderPartial looks like:
$this->renderPartial('_deleteSaved', array('savedLink'=>$s));
What I want being posted is being posted correctly but I'm not sure if it's communicating with the Controller and passing the data through or if my code for removing the data from the database is correct.
Any help would be greatly appreciated, thanks.

The problem is with array_slice part. As specified in php docs array slice does not modify array parameter.
Use array_splice instead (it modifies passed array param) and array_search to get key:
if(in_array($savedLink, $current->links))
{
$key = array_search($savedLink, $current->links);
array_splice($current->links, $key, 0);
$current->save();
}
NOTE: If $current->links is embedded documents (objects) array, you might have to find $key and check if is in array some other way.

Related

Cannot foreach over an array of objects in a Laravel POST

I have an array of objects which have been posted from a Vue Axios function, which I wish to loop over and save into a database. They are answers to a question.
I have passed in $data which is the array of answer objects (each has a content, correct and mark property), and the $id of the question they belong to. When I return $data, it shows me the array of objects with all the correct properties. When I return $data[0], I can access the first object. But when I try and foreach as below, it complains that $content doesn't exist. Running count() on $data also errors. What is wrong here?
Route::post('answers/{id}', function (Request $data, $id) {
foreach ($data as $value) {
$post[] = [
'user_id' => 1,
'question_id' => $id,
'content' => $value->content,
'correct' => $value->correct,
'mark' => $value->mark
]);
}
Answer::save($post);
});
You are trying to iterate over the hole $request object, which is an instance of the Request class. To access the received values first get them:
// To get all the data
$data = $request->all();
// or..
// To get just a specific value
$data = $request->get('key');
// or..
// only a list of allowed elements
$data = $request->only('here', 'goes', 'your', 'keys');
So, in case your frontend are sending an array of items under the key items. Just get them like mentioned above:
$items = $request->get('items');
Then you can use the foreach():
$items = $request->get('items');
foreach($items as $item)
{
// your operations
}
You can read more about Retrieving Input, in the documentation.

Passing an argument to callback function working on an array

I'm using an external class (Zebra_cURL) to execute multiple HTTP GET requests. It worked in the following way:
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
$items[$key]['size'] = strlen($result->body);
}
So my callback should fill up my $items array with more info for each url (in my case - size of the page). So there is a missing $key variable.
This class supports extra parameters in the callbacks ($moreimfo in my case). BUT as I understand the data passing to each callback will be always the same.
$result object containing the original url info ($result->info['url']). So I COULD use it to find needed array element. However this looks too inefficient in case the size of an array will be big enough.
I think that I should find how to pass an array member key information for EACH callback execution. Is it possible without modifying the original class?
If you use the url as key in the $items array the solution could be something like
<?php
$items = [
'url0'=>array('url' => 'url0'),
'url1'=>array('url' => 'url1'),
'url2'=>array('url' => 'url2'),
'url3'=>array('url' => 'url3'),
];
$curl = new Zebra_cURL();
$curl->get(
array_keys($items),
function($result) use (&$items) {
$key = $result->info['url'];
$items[$key]['size'] = strlen($result->body);
}
);
using an anymous function that "Imports" the $items array via reference.
While it doesn't solve the original problem of passing a reference to the according array element to the callback, the following should be very fast (as noted in the comments, PHP Arrays are implemented using a hashtable).
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$lookup=array();
foreach($lookup as $k=>$v) {
$lookup[$v['url']]=$k;
}
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
global $lookup,$items;
$items[$lookup[$result->info['url']]]['size'] = strlen($result->body);
}
Probably you may consider using an OOP-approach, with the callback as a method, then the global-izing of the arrays shouldn't be necessary if you use $this->anyMember

yii ajaxLink is not passing variable to controller

I am trying to pass a variable from an ajaxLink into my controller but my controller is not getting the variable.
//view
$ids = Yii::app()->storedData->getIds();
foreach($ids as $id) {
echo 'ID '.$id .'<br />';
echo CHtml::ajaxLink(
'remove',
array('/storedInfo/remove'),
array(
'data' => array('removeItem' => $id),
));
}
//controller
public function actionRemove() {
var_dump($_GET['removeItem']); // RETURNS string(0) ""
die();
}
The ajax array you're passing to CHtml::ajaxLink ends up taking the whole array and passing it to CJavaScript::encode().
As you mentioned in your comments, the $id you were using to build your data array was a PHP object. When this got to CJavaScript::encode, things got wonky, and the results weren't what you were expecting. Extracting a string or numeric value from $id, rather that passing the whole object should take care of this, e.g.:
'data' => array('removeItem' => $id->value) // obviously you'll need to use the proper key to get the value
Or maybe something like this depending on the data structure:
'data' => array('removeItem' => settype($id, 'string'),
I edited out my answer because i admit it was not what the original asker wanted but for the sake of the comments, i leave my code here
$ids = Yii::app()->storedData->getIds();
foreach($ids as $id) {
echo 'ID '.$id .'<br />';
echo CHtml::ajaxLink(
'remove',
array('/storedInfo/remove', array('removeItem' => $id)),
);
}
Looks like CHtml::ajaxLink takes more parameters than you're sending.
public static string ajaxLink(string $text, mixed $url, array $ajaxOptions=array ( ), array $htmlOptions=array ( ))
Do you need to send along some $text before the $url?

Passing additional parameters to the Zend partialLoop View Helper

In a Zend view I can apply a partial template to an iterable element as follows:
$this->partialLoop('template.phtml', $iterable);
However inside the template, only the elements of the $iterable are available, is there another way of passing extra data to the partial?
I use
$this->partialLoop('template.phtml', array(
'data' => $iterable,
'otherVariable' => $otherVariable
);
Warning & Edit:
To be completly honest, I made a mistake. I guess that the code I proposed won't work. I mistaken it for the partial() helper. It won't work because of this part of the helper's class:
foreach ($model as $item) {
// increment the counter variable
$this->partialCounter++;
$content .= $this->partial($name, $module, $item);
}
It will iterate over the whole array instead of the "data" key. I don't get how the answer could be accepted :D Thanks to Nikolaus Dulgeridis for pointing that out.
You can't even post any extra data through $this->view because the point of partial is that it creates "clean" view instance - so that the assigned variables won't collide with your existing variables.
Possible options
- Extend the view helper with methods to set custom variables
- Iterate the array and reformat it to
array(
array('data' => $item1, 'id' => 1, 'totalCount' => 10) ,
array('data' => $item2, 'id' => 2, 'totalCount' => 10) ,
array('data' => $item3, 'id' => 3, 'totalCount' => 10) ,
)
- Use Registry to store the values.
Zend_Registry::set('partialLoopCount', $count);
$this->partialLoop($viewScript, $data);
- Dump partialLoop and use partial() instead
I prefer this solution.
$count = count($data);
foreach ($data as $key => $value) {
echo $this->partial($viewScript, array('item' => $value, 'position' => $key, 'count' => $count));
}
Inside the partial, you can access all of your view variables with:
$this->partialLoop()->view->myVariable
where myVariable is a normal view variable ($this->view->myVariable in the controller or
$this->myVariable in the view, which it's actually the same thing).
Basically, you retrieve the PartialLoop() object, then the view which called it, and then the variable itself.
This, though, will probably impact performance (and I don't think it's really MVC friendly...)
But, hey: it works. :)
An example here:
Hardcode.nl == Joris Osterhaus
Later I found (insert in partial):
$this->getHelper('PartialLoop')->view->otherVariable;
You can access parent view variables by this way :
$this->ViewModel()->getCurrent()->getVariable('parentVariable');
Found at http://blog.dossantos.com.au/how-to-access-parent-view-variables-from-a-partial-loop-in-zf2
In controller
$this->view->otherVariable = 'otherVariable';
In "partial file" - template.phtml
$this->otherVariable
(ZendFramework-1.11.4-minimal)

Is there an advantage to using parse_str for optional function parameters vs an array?

I happened to be making some changes to a WordPress blog and noticed that they use parse_str (http://php.net/parse_str) for parsing and setting their optional parameters to a function.
I'm wondering if there is an advantage to this over sending an array?
Examples:
With array:
$blahOptions = array(
'option_1' => true,
);
BlahArray($blahOptions);
function BlahArray($options = array()) {
$defaults = array(
'option_1' => false,
'option_2' => 'blah',
);
// this would probably be in a function to be used everywhere
foreach ($defaults as $defaultOption => $defaultValue) {
if (!isset($options[$defaultOption])) $options[$defaultOption] = $defaultValue;
}
}
With parse_str:
$blahOptions = 'option_1=1';
BlahString($blahOptions);
function BlahString($options = '') {
$defaults = array(
'option_1' => false,
'option_2' => 'blah',
);
parse_str($options, $defaults);
$options = $defaults;
}
No. That seems like a ridiculous way to pass functional parameter arguments. I could understand it if you needed to recreate $_GET or $_POST variables or something along those lines, but for parameters to a function? That's code smell right there.
They should be using an array, and then utilizing extract() instead. I've worked with Wordpress before, and my advice is to keep the code at arm's length. It is not an example of model programming.
No. There are more disadvantages than advantages.
When you’re using a single string, you just can pass string values. With an array you can use every PHP data type and every element’s value type is independently of each other.
With parse_str, you can potentially drop in the query string from the URL, and the function will work. If you use an array, and you want to use the query string, you'll have to enumerate everything into an array before calling the function.
I'm not totally convinced it's the best way to go, but I see how it can add a bit of flexibility.

Categories