Foreach logic issue with CakePHP - cakephp

I have I problem that I hope someone can help me with. I thought this code was right, but it will not work. Below is my code, it is a function for my CakePHP 2.2.2 site, the main aim of the code is to produce a menu system from database results. The problem is with my foreach loop, it will not loop. All $Key does is return the value of 2 (three records within the table at this time). So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
I know the SQL command is right, if that is debuged / echoed then all three results are displayed. The idea of this loop was to get it to count the results, so that I could run a check on a selected field. Where I am going wrong?
function MenuSystem() {
$this->loadModel('Menu');
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $Key=>$Value) {
$MenuSystem = $MenuSQL[$Key];
$this->Menu = $MenuSystem;
}
}
Many Thanks,
Glenn.
UPDATE :::
Below is my function, now my foreach loop now works, don't know what I was doing wrong, but I know think its working. You can see the print_r command that I am using for testing, if I use that, then all links from my database are printed / echoed on the screen and all works. But if I try and call the $this->Menu from another controller, then only the last record is echoed on the screen. I have moved the $this->Menu outside of the foreach loop, but it made no difference, with it inside the loop or outside, it still only echoes the last record and not all three. So what I am doing wrong?
function MenuSystem() {
$this->loadModel('Menu');
$SiteBase = '/projects/cake/';
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $key => $Value) {
$MenuAccessLevel = $MenuSQL[$key]['Menu']['roles_id'];
if ($MenuAccessLevel == 1) {
$Path = $MenuSQL[$key]['Menu']['path'];
$Title = $MenuSQL[$key]['Menu']['title'];
$MenuSys = "<a href=\" " . $SiteBase . $Path . " \">" . $Title ."";
} else {
print ("Admin");
}
//print_r($MenuSys);
} //End of Foreach Loop
$this->Menu = $MenuSys;
} //End of function MenuSystem

So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
You're setting the value of $this->Menu within the foreach, so when the foreach is complete it will take the last value iterated over.
If you want to find the number of records matching a condition, try:
$menuCount = $this->Menu->find('count', array(
'conditions'=>array('active'=>true)
));
$this->set(compact('menuCount'));
Edit: also, by setting the value of $this->Menu within the foreach, you're overwriting the Menu model variable. This is not a good idea.
Edit2: to get the counts of rows as grouped by some value, try:
$this->Menu->virtualFields = array('count' => 'count(*)');
$counts = $this->Menu->find('all', array(
'group'=>'Role',
'fields'=>array('Role', 'count'),
));
This generates SQL to have the results grouped by the Role column. Returned fields are the name of the role, and the number of rows having that value.
If you wanted to do it with a foreach loop instead, it might look like:
$menus = $this->Menu->find('all', array('fields'=>array('id', 'Role')));
$counts = array('user'=>0, 'admin'=>0);
foreach ($menus as $menu) {
$role = $menu['Menu']['Role'];
$counts[$role] += 1;
}

Related

Use results from one array in another array

I have a logged in user following other users in a blog. I would like my logged in users to see posts from users they follow.
I run a query that returns a list of user_ids (user_id1) a logged in user (user_id2) follows in an array. I then run the array through a foreach loop to get the list of users as user ids and place it in a new array. The returned user1_ids are echoed as a string. I would now like to use the returned user1_ids in another array that only displays posts based on the user1_ids.
The issue is that the values are returned as a string and my second array used in the posts only reads the first number of the first array as the values are returned as a string and not integers.
How can I use the results from my first array in my second array 'author__in'=> array()? Do I need to convert the string into integers or is there a better method?
// The database query that returns the array
<?php
$currentloggedinuser = get_current_user_id();
$followers_query = $wpdb->get_results("SELECT ID, user_id1 FROM wp_followers WHERE user_id2 = '$currentloggedinuser' ") ?>
// the returned array from the query through foreach placed in another array
<?php
$following_id = array();
foreach ($followers_query as $follower) {
$following_id[] = $follower->user_id1;
sort($following_id);
$following_ids = implode(", ",$following_id);
}
?>
// the resulting number values returned as a string
<?php echo $following_ids; ?>
<?php
$args = array(
'author__in'=> array($following_ids), // user1_ids I'd like to include from the array above
'post_type' => 'post'
);
This would probably work. I did not test it but it should be fine.
Here is some info on the $wpdb::get_col method
<?php
$currentloggedinuser = get_current_user_id();
// Get an array containing only the user_id1 values.
// It's a good habit to use the prepare method of $wpdb
// for security and ease of reading the code
// Also, you should use $wpdb->prefix instead of using the 'wp_' for the table name
// that prefix can be changed and your code would break.
$followers = $wpdb->get_col(
$wpdb->prepare(
"SELECT user_id1 FROM {$wpdb->prefix}followers WHERE user_id2 = %d",
$currentloggedinuser
)
);
sort($followers); // or you can just apply sorting in the query above
// Directly echo the imploded array.
// No need to store it in a variable to do so unless you want to use it somewhere else
echo implode(', ', $followers);
$args = array(
'author__in'=> $followers, //use the array we got from the query
'post_type' => 'post'
);

Cake PHP 3 needs limit option for find all method

Inside a cell I need to access the TreeOptions model.
So I've wrote this :
$this->loadModel( 'TreeOptions' );
$i = $this->TreeOptions->find( 'all' );
But when I do the foreach like this :
foreach( $i as $row )
debug( $row->description );
It only returns the last record of the result.
The only way I've found to make it work as desired is adding the limit clause :
$i = $this->TreeOptions->find( 'all', [ 'limit' => 200 ] );
And then, I can get the whole set of records.
What am I missing ?
Thanks.
Regards.
In your first snippet, the variable $i, is a state where the query has not yet run. See the excerpt from CakePHP 3 Cookbook: Retrieving Data & Results — Using Finders to Load Data:
// Find all the articles.
// At this point the query has not run.
$query = $articles->find('all');
// Iteration will execute the query.
foreach ($query as $row) {
}
// Calling all() will execute the query
// and return the result set.
$results = $query->all();
// Once we have a result set we can get all the rows
$data = $results->toArray();
// Converting the query to an array will execute it.
$results = $query->toArray();

Accessing array values of a field collection in a node with Drupal?

Please bare with a very recent user of Drupal.
I want to create an array out of all examples of the string "url" on a Drupal site.
I've used the method "field_get_items" previously to do something very similar, but I am now trying to access a field collection that is many levels deep into the node's array and I'm not sure that method would work.
$website_urls = array();
$faculty_members = field_get_items('node', $node, 'field_faculty_member');
for ($i = 0; $i < count($faculty_members); $i++) {
$value = field_view_value('node', $node, 'field_faculty_member', $faculty_members[$i]);
$field_collection = $value['entity']['field_collection_item'][key($value['entity']['field_collection_item'])];
$website_urls[] = render($field_collection['field_link']['#items'][0]['url']);
}
An example of one url's location is...
['field_faculty_program'][0]['entity']['field_collection_item'][1842]['field_faculty_member'][0]['entity']['field_collection_item'][1843]['field_link']['#items'][0]['url']
..and another...
['field_faculty_program'][4]['entity']['field_collection_item'][1854]['field_faculty_member'][0]['entity']['field_collection_item'][1855]['field_link']['#items'][0]['url']
What is the method I should be using to collect al of the 'url' strings for placement in an array?
You can actually still use the field_get_items() function but eventually pass it 'field_collection_item' instead for the node type.
Something like this should work:
if ($items = field_get_items('node', $node, 'field_faculty_member')) {
//loop through to get the ids so we can take
//advantage of field_collection_item_load_multiple for
//greater efficiency
$field_collection_item_ids = array();
foreach ($items as $item) {
$field_collection_item_ids[] = $item['value'];
}
if ($field_collection_items = field_collection_item_load_multiple($field_collection_item_ids)) {
foreach ($field_collection_items as $subitem) {
//now we load the items within the field collection
if ($items = field_get_items('field_collection_item', $subitem, 'field_faculty_member')) {
//And you can then repeat to go deeper and deeper
//e.g. a field collection item within a field collection
//for instance to get the urls within your faculty members
//item. Best to break this into functions or a class
//to keep your code readable and not have so many nested
//if statements and for loops
}
}
}
}
Hope that helps!
Scott

CakePhp foreach saving only last value in array

I have a problem, right now Im using this foreach loop on CakePhp on which I want to add all the values which are still not on the table for the respecting user. To give a little more context, the user has a menu. And the admin can select which one to add for the user to use. On the next code I receive a array with the menus which will be added as so:
//This is what comes on the ['UserMenuAccessibility'] array:
Array ( [menu_accessibility_id2] => 2 [menu_accessibility_id3] => 3 [menu_accessibility_id4] => 4 [menu_accessibility_id5] => 5 [menu_accessibility_id8] => 8 )
I get the ids of the menus which want to be added to the table for the user to use. And I use the next code to add the menus to the table if they are not there still:
//I check if the array has something cause it can come with no ids.
if (!(isset($this->request->data['UserMenuAccessibility']))) {
$this->request->data['UserMenuAccessibility'] = array();
}
$UserMenuAccessibility = $this->request->data['UserMenuAccessibility'];
foreach ($UserMenuAccessibility as $key => $value) {
$conditions = array(
'UserMenuAccessibility.menu_accessibility_id' => $value,
'UserMenuAccessibility.users_id' => $id
);
if ($this->User->UserMenuAccessibility->hasAny($conditions)) {
} else {
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
For some reason the array is only saving the last new id which is not on the table and not the rest. For example if I have menu 1 and 2 and add 3 and 4 only 4 gets added to the table. For some reason I cant add all the missing menu ids to the table. Any ideas why this is happening?
Thanks for the help on advance.
It looks like your code will save each item, but each call to save() is overwriting the last entry added as $this->User->UserMenuAccessibility->id is set after the first save and will be used for subsequent saves. Try calling $this->User->UserMenuAccessibility->create() before each save to ensure that the model data is reset and ready to accept new data:-
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
$this->User->UserMenuAccessibility->create();
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
}
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data
In cakephp3, patchEntity() is normally used. However, when using it for inserting-new/updating entries in a foreach loop, I too saw that it only saves the last element of the array.
What worked for me was using patchEntities(), which as explained in the patchEntity() doc, is used for patching multiple entities at once.
So simplifying and going by the original code sample to handle multiple entities, it could be:
$userMenuAccessibilityObject = TableRegistry::get('UserMenuAccessibility');
foreach ($UserMenuAccessibility as $key => $value) {
$userMenuAccessibility = $userMenuAccessibilityObject->get($value);//get original individual entity if exists
$userMenuAccessibilities[] = $userMenuAccessibility;
$dataToPatch = [
'menu_accessibility_id' => $value,
'users_id' => $id
]//store corresponding entity data in array for patching after foreach
$userMenuAccessibilitiesData[] = $dataToPatch;
}
$userMenuAccessibilities = $userMenuAccessibilityObject->patchEntities($userMenuAccessibilities, $userMenuAccessibilities);
if ($userMenuAccessibilityObject->saveMany($requisitions)) {
} else {
$this->Session->setFlash(__('The users could not be saved. Please, try again.'));
}
Note: I haven't made it handle if entity doesn't exist, create a new one and resume. That can be done with a simple if condition.

multiply entries with a single add

I'm trying to add a repeat function to my Events/add.
So if I have the same Event from Monday to Friday (or even more days) the user can add everything in a single add, instead than one by one.
I added an input field in Events/add.ctp which is:
$this->Form->input('repeat', array('type' => 'number'));
to compare it in a do - while loop in beforeSave()
if(!empty($this->data[$this->alias]['repeat'])) {
$repeat = $this->data['Event']['repeat'];
$i = 0;
do {
$start = strtotime($this->data[$this->alias]['start'] . ' +1 day');
$end = strtotime($this->data[$this->alias]['end'] . ' +1 day');
$this->data[$this->alias]['start'] = date('Y-m-d H:i:s', $start);
$this->data[$this->alias]['end'] = date('Y-m-d H:i:s', $end);
$this->create();
$this->save($this->data);
//i think here's the problem... this shouldn't be done here right?
$i++;
} while ($repeat >= $i);
}
I ran out of ideas on how to make this possible..
I know that I have to use beforeSave to handle data after the add action, but obviously I cannot save data in before save... any hints?
Definitely don't do it in beforeSave. As you said, is kind of weird having a save inside a beforeSave... Hasn't it give you loop problems even? Because you do a save inside the loop, and $this->data keeps having the same repeat value inside... it will be repeating forever and spaming that poor events table.
No, definitely get that loop out of there.
I think the best way to do this is to add a new function in your model
public function saveRepeating($data) {
if(!empty($data[$this->alias]['repeat'])) {
$repeat = $data['Event']['repeat'];
unset($data['Event']['repeat']); //prevent loops!
$i = 0;
do {
$start = strtotime($data[$this->alias]['start'] . ' +1 day');
$end = strtotime($data[$this->alias]['end'] . ' +1 day');
$data[$this->alias]['start'] = date('Y-m-d H:i:s', $start);
$data[$this->alias]['end'] = date('Y-m-d H:i:s', $end);
$this->create();
$this->save($data);
$i++;
} while ($repeat >= $i);
}
}
And in your controller, call that function instead of save with
$this->Model->saveRepeating($data);
Notice I didn't really change anything inside your loop, just $this->data for $data. But having it as a separate function avoids any weird problems with beforeSave. Oh, and I added an unset to the repeat variable to prevent any loop (delete that unset if you want to save the repeat value to a table, though). You can add a return value to validate that every event got saved, or handle exceptions, etc.
Remember to return the beforeSave as it was before!

Resources