I'm developing a simple application with CakePhp, and need some help on creating a multi-record edit form using some related data.
The application I'm developing is pretty straightforward, its main purpose is managing students records: updating, deleting, changing a student from one group to another, the usual suspects.
The relevant tables of the database are:
group = (id, teacher, classroom, etc)
groups_students=(groupID, studentID, since, until)
students = (id, name, last_name, etc)
assitance = (id, assitance, date)
assistance_students (studentID, assitanceID, meta_information )
As you might have gathered from the tables above, the application is supposed to aid in recording assitance. Which is where I'm having some issues.
What I want to do is this:
Have the user select a group
In de detail group, I'll have an action called "Register Assistance"
Register assitance should redirect to a view in wich for every student belonging to that group, the user can see the student's assitance, edit them, and save. Something like this:
In which A stands for "Absent" and P for "Present" and the user can edit everyone, and the save.
I just don't know how to go about that? How do I manage that? I've managed to create a multi-edit form for the assitance, but adding the related data is a pain, I don't know if I should query the students from the groups controllers and then pass that to the action to register assitance, or manage all the logic inside the assitance controller?
Any help would be great,
thanks!
Edit: Here's the output of $this->Student->find('first');
Array (
[Alumno] => Array (
[id] => 14
[tipo] => dni
[dni] => 2321312312
[apellido] => COQUITO
[nombre] => Pepe
[carrera] => ComposiciĆ³n Musical
[creado] => 2011-01-08 17:59:00
[modificado] => 2011-01-08 17:59:00
)
)
The output is in spanish. Alumno = Student, nombre= first name, apellido= last_name.
Well, I managed to find a solution to this. I was waiting to see if anyone found something better, as I'm pretty sure my solution is far from the best, but here it goes, in case someone finds this question in the future.
My issue was that I needed, for every group I selected to register assitance:
Every student that belonged to that group
All the assitances of the students belonging to that group
Now, in order to make use of the Form helper to edit those assitences (and after, the saveAll() method), I needed $this->data to have ann array like this:
Array
(
[Assitance] => Array
(
[5] => Array
(
[id] => 5
[date] => 2011-01-09
[assitance] => A
[updated] => 2011-01-16 21:32:00
[created] => 2011-01-16 21:32:00
)
[6] => Array
(
[id] => 6
[date] => 2011-03-09
[assitance] => A
[updated] => 2011-01-16 21:32:00
[created] => 2011-01-16 21:32:00
)
)
)
at the same time, though, I needed each student linked to his or her assitances, to be able to loop trough them, and display the edit forms for every assitance in a table like the one in the question:
What I ended up doing is this
First, I query all the students belonging to the selected group.
For each student get all his/her assitances .
so, for each student, I ended up having an array like this:
Array
(
[Student] => Array
(
[id] => 12
[last_name] => LASARTE
[name] => Julia
[created] => 2011-01-08 16:35:00
[updated] => 2011-01-08 16:35:00
[assitance] => Array
(
[0] => Array
(
[Assitance] => Array
(
[id] => 4
[date] => 2011-01-09
[assitance] => z
[updated] => 2011-01-16 20:51:00
[created] => 2011-01-16 20:51:00
)
[assitance_studenty] => Array
(
[id] => 2
[student_id] => 12
[assitance_id] => 4
[comision] => 0
)
)
)
)
)
)
Now, with that, I have the information I need in order to display the table and create the forms, but I still need the assitances data in $this->data, so the form helper can create the from displaying the correct information, and afterwars, saveAll() updates the rows in the database correctly.
What I needed to do was turn the array of $students into an structure like the one at the beginning of the post. Enter the Set Class:
$formated_students = Set::combine($students, '{n}.students.id', '{n}.students');
$assitance = Set::extract('/presentes/Assistance', $formated_students);
$this->data['Assistance'] = Set::combine($assistance, '{n}.Assistance.id', '{n}.Assistance');
I'm pretty sure the first to lines (formating the students array in order to extract the assitance) can be done with just one Set::extract or Set::classicExtract, but this worked and regular expressions are really not my thing, so.
After that, It's just a matter of looping trough the data and making the table:
<?php
echo $form->create('Assistance', array('url' => array('controller' => 'Comisions', 'action' => 'register_assitance')));
foreach ($students as $student) { ?>
<tr><td>><?php echo $student['students']['last_name'].", ".$student['students']['name']; ?></td>
<?php foreach ($student['students']['assitance'] as $asstiance) { ?>
<td><?php echo $form->input('Assistance.'.$assitance['Assistance']['id'].'.id');
echo $form->input('Assistance.'.$assitance['Assistance']['id'].'.assistance', array( 'label' => false, 'size' => 1)) ?></td>
<?php } ?>
</tr>
<?php } ?>
Related
I am having an issue trying to build an array where the status ID is the key and ALL posts related to the statuses are sub-arrays relating to the key (status ID).
Here's the (incorrect) array I am getting with both array_merge_recursive and manually adding items to the array (array 1):
Array
(
[res_1] => Array
(
[status_name] => NEEDS REVIEW
[status_color] => 666666
[post] => Array
(
[post_title] => Add feature that allows our team to add internal notes on ideas
[post_description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[categories] => Admin,Communication
)
)
[res_2] => Array
(
[status_name] => PLANNED
[status_color] => aa5c9e
[post] => Array
(
[post_title] => Add support for multiple languages
[post_description] => We have customers across the globe who would appreciate this page to be localized to their language
[categories] => Integrations
)
)
[res_3] => Array
(
[status_name] => IN PROGRESS
[status_color] => 3333cc
[post] => Array
(
[post_title] => Allow users to add an image with their feature request
[post_description] => Sometimes users want something visual, having an example really helps.
[categories] => Uncategorized
)
)
[res_4] => Array
(
[status_name] => COMPLETED
[status_color] => 7ac01d
[post] => Array
(
[post_title] => Add feature that allows #mentioning in comments
[post_description] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[categories] => Communication
)
)
)
Here's something like what I am expecting to happen (every status ID is an array with multiple posts as sub-arrays):
Array
(
[res_1] => Array
(
[status_name] => NEEDS REVIEW
[status_color] => 666666
[post] => Array
(
[post_title] => Add feature that allows our team to add internal notes on ideas
[post_description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[categories] => Admin,Communication
)
)
[res_2] => Array
(
[status_name] => PLANNED
[status_color] => aa5c9e
[post] => Array
(
[post_title] => Add support for multiple languages
[post_description] => We have customers across the globe who would appreciate this page to be localized to their language
[categories] => Integrations
)
)
[res_3] => Array
(
[status_name] => IN PROGRESS
[status_color] => 3333cc
[post] => Array
(
[post_title] => Allow users to add an image with their feature request
[post_description] => Sometimes users want something visual, having an example really helps.
[categories] => Uncategorized
)
)
[res_4] => Array
(
[status_name] => COMPLETED
[status_color] => 7ac01d
[post] => Array(
[0] => Array (
[post_title] => Add feature that allows #mentioning in comments
[post_description] => There is no hierarchy in comments so its hard to reply to one specific user if there is a longer thread of comments.
[categories] => Communication
)
[1] => Array (
[post_title] => Feature Number 5
[post_description] => lorum ipsum awesomeness.
[categories] => Admin
)
)
)
Here's what I've tried:
Running two separate DB queries: one to fetch statuses and another to fetch posts then merging the arrays recursively and changing the array keys to a string. This does the same thing, post 5 never shows up in the newly merged array.
Same as above - ran two separate queries and rebuilt the array manually, the same result the 5th post never appears.
I printed out the database result from $stmt2->fetchAll(); all 5 posts are there in the result-set directly from the database. The 5th one just won't persist when merging arrays or building a fresh one so the posts can relate to the statuses.
I also tried joining the tables with SQL but even grouping by resolution_id does the same thing, post number 5 gets lost by the grouping. I've tried sub-queries too.
DB Results array for just posts:
Array
(
[0] => Array
(
[title] => Feature number 5
[0] => Feature number 5
[description] => lorum ipsum awesomeness
[1] => lorum ipsum awesomeness
[resolution_id] => 4
[2] => 4
[category_names] => Admin
[3] => Admin
)
[1] => Array
(
[title] => Allow users to add an image with their feature request
[0] => Allow users to add an image with their feature request
[description] => Sometimes users want something visual, having an example really helps.
[1] => Sometimes users want something visual, having an example really helps.
[resolution_id] => 3
[2] => 3
[category_names] => Uncategorized
[3] => Uncategorized
)
[2] => Array
(
[title] => Add support for multiple languages
[0] => Add support for multiple languages
[description] => We have customers across the globe who would appreciate this page to be localized to their language
[1] => We have customers across the globe who would appreciate this page to be localized to their language
[resolution_id] => 2
[2] => 2
[category_names] => Integrations
[3] => Integrations
)
[3] => Array
(
[title] => Add feature that allows #mentioning in comments
[0] => Add feature that allows #mentioning in comments
[description] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[1] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[resolution_id] => 4
[2] => 4
[category_names] => Communication
[3] => Communication
)
[4] => Array
(
[title] => Add feature that allows our team to add internal notes on ideas
[0] => Add feature that allows our team to add internal notes on ideas
[description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[1] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[resolution_id] => 1
[2] => 1
[category_names] => Admin,Communication
[3] => Admin,Communication
)
)
Since the data is always going to be dynamic (users can choose status names and create as many as they need to) I can't just hard-code the status names/ids and run 4 queries to populate the columns.
To prevent this from being an essay long post, here are the bits of code that are building the array from array 1:
Builds the initial statuses array from the query results from the resolutions table.
$statuses = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$statuses['res_' . $row['id']] = ['status_name' => $row['name'], 'status_color' => $row['color']];
}
Adds the individual posts to the statuses array:
foreach ($dbposts as $row2) {
$statuses['res_' . $row2['resolution_id']]['post'] = ['post_title' => $row2['title'], 'post_description' => $row2['description'], 'categories' => $row2['category_names']];
}
The resolution ID is concatenated with res_ from when I tried doing an array merge based on keys. It would not merge when the keys were just integers.
Finally some context behind why I am trying to do what I am trying to do. I am building a platform where companies can have users submit feature requests and view the results in a list view or board view. The list view was a piece of cake since the board view needs to be per status, this is where I am having trouble. I hard-coded the board view values to demonstrate the expected end-result:
Not looking for someone to write my code for me, just looking for some guidance - perhaps I am building or merging the arrays wrong?
To build the array of posts, you need to append elements to the array of posts. Currently, you are just assigning a single element to the array over and over, which overwrites the previous value of the entire array.
The code to append posts:
$statuses['res_' . $row2['resolution_id']]['post'][] = ['post_title' => $row2['title'], 'post_description' => $row2['description'], 'categories' => $row2['category_names']];
Note the [] which I added to the end of the left side of the assignment operator.
I have a Course model and a User model joined by Subscription. I want to create a postLink form helper on the Courses index page that would automatically subscribe the user to that, but I don't know how to pass the parameters correctly. In essence, I want the user to click Subscribe, and have a subscriptions/add form automatically submitted so that the join table has another record.
My specific question is: How do I use a model in another model's view?
Here's the Courses index.ctp array from print_r:
Array
(
[0] => Array
(
[Course] => Array
(
[id] => 1
[name] => Flying
[created] => 2014-01-27 19:05:43
[modified] => 2014-01-27 19:05:43
)
[Subscription] => Array
(
[0] => Array
(
[id] => 2
[user_id] => 2
[course_id] => 1
)
)
)
)
Here's my bad postLink in the Course index.ctp view:
<?php
echo $this->Form->postLink(__('Subscribe'),
array( 'controller' => 'Subscriptions',
'action' => 'add' ),
null,
__('Are you sure you want to subscribe to # %s?',
$course['Course']['name']));
?>
Assuming they're associated, you can do like you would in the Controller - just don't include the model you're actually in, since $this is already that model:
$this->AssociatedModel->save($data);
I'm working on a facebook app to retrieve information from the user. The overall goal is to make a "interactive story" about the user. So I want the name, birthday etc. But I also want to know their favorite bands. The problem is that I can display the array with all the info needed. But that is to much. I don't want to know the category or the id, I only want to know the name.
$user_music = $facebook->api('/me/music');
print_r ($user_music);
If I use this code, I get something like this:
Array ( [data] => Array (
[0] => Array ( [category] => Musician/band [name] => Red Hot Chili Peppers [created_time] => 2011-03-21T15:01:57+0000 [id] => 8335563918 )
[1] => Array ( [category] => Musician/band [name] => Train [created_time] => 2011-03-21T15:01:57+0000 [id] => 15313895735 ) )
I only want the bands name, but if I use this code:
$user_music = $facebook->api('/me/music');
$music_name = $user_music["name"];
echo $music_name;
it says it doesn't know "name".
You missed 1 array level. To access "name" you should use:
$user_music["data"][0]["name"]
or to get all names you should iterate
foreach($user_music["data"] as $data) {
echo $data["name"];
}
you should do something like this:
$user_music['data'][0]["name"]
or run $user_music['data'] in a foreach loop
Let me be up front: I'm a PHP hack. There's probably some stupid mistakes in here. Please point them out if you see them.
What I'm trying to do: I'm creating a page for a restaurant that would like their Yelp reviews displayed. I'm using the Yelp Phone API to grab the reviews for the specific business. Please view the sample response on the Yelp API documentation located here: http://www.yelp.com/developers/documentation/phone_api#sampleResponse
What I've done:
Successfully connected to the API and returned a response
echoed values from the response array in a foreach loop.
If you view the documentation, you can see there are a few levels of the response. I can easily print, echo, whatever values from the second tier, but what I'm really after is all nested in the "reviews" section of the response. I'm having trouble figuring out how to echo the values within the reviews section (eg user_name, review_excerpt etc).
My Code:
$yelpstring = file_get_contents("http://api.yelp.com/phone_search?phone=[redactedphonenumber]&ywsid=[redactedapikey]", true);
$obj = json_decode($yelpstring);
foreach($obj->businesses as $key => $business)
{
$reviews = $business->reviews;
//print_r($reviews);
echo $reviews['user_name'];
}
If I echo $reviews, I just get the word "Array". If I print_r($reviews), I get an expected list of keys and values. If I try to echo a specific value from the array(echo $reviews['user_name'], I get nothing. Any light shed on what I'm doing wrong would be greatly appreciated. I'm sure I'm missing something simple. Thank you for your time!
Edit: print_r($reviews) output:
Array ( [0] => stdClass Object ( [rating_img_url_small] => http://media4.px.yelpcdn.com/static/201012164278297776/img/ico/stars/stars_small_2.png [user_photo_url_small] => http://media2.px.yelpcdn.com/static/201012162819681786/img/gfx/blank_user_extra_small.gif [rating_img_url] => http://media4.px.yelpcdn.com/static/201012163489049252/img/ico/stars/stars_2.png [rating] => 2 [user_url] => http://www.yelp.com/user_details?userid=vZbcPrYPSMFIDIfTub5H1g [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:u9ckRV6tKApe6Bu93M93CA [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=u9ckRV6tKApe6Bu93M93CA [text_excerpt] => I wanted to like this place. It's got the contemporary name and it's full of hipsters. The place looked clean and the style was fun and cute. I felt like... [user_photo_url] => http://media3.px.yelpcdn.com/static/201012161186834854/img/gfx/blank_user_small.gif [date] => 2011-09-07 [user_name] => boycott p. [id] => u9ckRV6tKApe6Bu93M93CA ) [1] => stdClass Object ( [rating_img_url_small] => http://media4.px.yelpcdn.com/static/201012164278297776/img/ico/stars/stars_small_2.png [user_photo_url_small] => http://media1.px.yelpcdn.com/upthumb/MWu84G5QtmBmT9GoqjT_kg/ss [rating_img_url] => http://media4.px.yelpcdn.com/static/201012163489049252/img/ico/stars/stars_2.png [rating] => 2 [user_url] => http://www.yelp.com/user_details?userid=izF2cGrmqt-u_Z2tDZ8dbg [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:OYLeeCMgnpZkk1c9LWu97g [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=OYLeeCMgnpZkk1c9LWu97g [text_excerpt] => Food is decent and overpriced, but service is a joke. Your food will take a minimum of 20 minutes, for the basic breakfast. Then when your food does come... [user_photo_url] => http://media1.px.yelpcdn.com/upthumb/MWu84G5QtmBmT9GoqjT_kg/ms [date] => 2011-09-06 [user_name] => April H. [id] => OYLeeCMgnpZkk1c9LWu97g ) [2] => stdClass Object ( [rating_img_url_small] => http://media2.px.yelpcdn.com/static/20101216418129184/img/ico/stars/stars_small_4.png [user_photo_url_small] => http://media1.px.yelpcdn.com/upthumb/3euzdGdLZRFxImY68MSg7w/ss [rating_img_url] => http://media2.px.yelpcdn.com/static/201012164084228337/img/ico/stars/stars_4.png [rating] => 4 [user_url] => http://www.yelp.com/user_details?userid=bHR9UU4vtx2QKZD44O0E5g [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:njvNAzfSII3PxXyUymLZ1w [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=njvNAzfSII3PxXyUymLZ1w [text_excerpt] => Stopped here for breakfast on a friday morning. We were seated immediately and had a really friendly waitress. I ordered a side order of the Chai french... [user_photo_url] => http://media1.px.yelpcdn.com/upthumb/3euzdGdLZRFxImY68MSg7w/ms [date] => 2011-09-05 [user_name] => Diane F. [id] => njvNAzfSII3PxXyUymLZ1w ) )
Based on the output of print_r you can't reference $reviews['user_name'];
Note that $reviews is an Array of objects. So to access user_name you need to use
echo $reviews[0]->user_name;
And if you have more than one item in the array, you will need a loop like
for ($i = 0; $i<count($reviews); $i++) {
echo $reviews[$i]->user_name;
}
I hope this helps.
$reviews is an array of review objects. You'll need to loop over it to get to the data you're after.
I am playing around with a quotes database relating to a ski trip I run. I am trying to list the quotes, but sort by the person who said the quote, and am struggling to get the paginate helper to let me do this.
I have four relevant tables.
quotes, trips, people and attendances. Attendances is essentially a join table for people and trips.
Relationships are as follows;
Attendance belongsTo Person hasMany Attendance
Attendance belongsTo Trip hasMany Attendance
Attendance hasMany Quote belongs to Attendance
In the QuotesController I use containable to retrieve the fields from Quote, along with the associated Attendance, and the fields from the Trip and Person associated with that Attendance.
function index() {
$this->Quote->recursive = 0;
$this->paginate['Quote'] = array(
'contain' => array('Attendance.Person', 'Attendance.Trip'));
$this->set('quotes', $this->paginate());
}
This seems to work fine, and in the view, I can echo out
foreach ($quotes as $quote) {
echo $quote['Attendance']['Person']['first_name'];
}
without any problem.
What I cannot get to work is accessing/using the same variable as a sort field in paginate
echo $this->Paginator->sort('Name', 'Attendance.Person.first_name');
or
echo $this->Paginator->sort('Location', 'Attendance.Trip.location');
Does not work. It appears to sort by something, but I'm not sure what.
The $quotes array I am passing looks like this;
Array
(
[0] => Array
(
[Quote] => Array
(
[id] => 1
[attendance_id] => 15
[quote_text] => Hello
)
[Attendance] => Array
(
[id] => 15
[person_id] => 2
[trip_id] => 7
[Person] => Array
(
[id] => 2
[first_name] => John
[last_name] => Smith
)
[Trip] => Array
(
[id] => 7
[location] => La Plagne
[year] => 2000
[modified] =>
)
)
)
I would be immensely grateful if someone could suggest how I might be able to sort by the the first_name of the Person associated with the Quote. I suspect my syntax is wrong, but I have not been able to find the answer. Is it not possible to sort by a second level association in this way?
I am pretty much brand new with cakephp so please be gentle.
Thanks very much in advance.
I've had the similar problem awhile back. Not with sort though. Try putting the associated table in another array.
echo $this->Paginator->sort('Name', 'Attendance.Person.first_name');
change to:
echo $this->Paginator->sort('Name', array('Attendance' => 'Person.first_name'));
Hope this helps
i'm also looking for help with this.
so far i've found that you can sort multi level associations in controller's pagination options after using the linkable plugin https://github.com/Terr/linkable.
but it breaks down when you try to sort form the paginator in the view. i'm using a controller for magazine clippings. each clipping belongs to an issue and each issue belongs to a publication.
$this->paginate = array(
"recursive"=>0,
"link"=>array("Issue"=>array("Publication")),
"order"=>array("Publication.name"=>"ASC",
"limit"=>10);
after debugging $this->Paginator->params->paging->Clipping in the view, you can see that the sort is described in two separate places, "defaults" and "options". the sort info needs to be present in both for it to work in the view.
here is after setting order in controller:
[Clipping] => Array
(
[page] => 1
[current] => 10
[count] => 6685
[prevPage] =>
[nextPage] => 1
[pageCount] => 669
[defaults] => Array
(
[limit] => 10
[step] => 1
[recursive] => 0
[link] => Array
(
[Issue] => Array
(
[0] => Publication
)
)
[order] => Array
(
[Publication.name] => ASC
)
[conditions] => Array
(
)
)
[options] => Array
(
[page] => 1
[limit] => 10
[recursive] => 0
[link] => Array
(
[Issue] => Array
(
[0] => Publication
)
)
[order] => Array
(
[Publication.name] => ASC
)
[conditions] => Array
(
)
)
)
and here is after using $this->Paginator->sort("Publication","Publication.name");.
notice the options array is empty.
[Clipping] => Array
(
[page] => 1
[current] => 10
[count] => 6685
[prevPage] =>
[nextPage] => 1
[pageCount] => 669
[defaults] => Array
(
[limit] => 10
[step] => 1
[recursive] => 0
[link] => Array
(
[Issue] => Array
(
[0] => Publication
)
)
[order] => Array
(
[Publication.name] => DESC
)
[conditions] => Array
(
)
)
[options] => Array
(
[page] => 1
[limit] => 10
[recursive] => 0
[link] => Array
(
[Issue] => Array
(
[0] => Publication
)
)
[order] => Array
(
)
[conditions] => Array
(
)
)
does one really need to modify the paginator class to make this work?
UPDATE:
i found out the problem:
in the core cake controller paginator merges default and options to create the find query.
but the options array is empty when using linkable to sort. because options is listed after default it overrides default and the empty array replaces the default array of options.
solution to this is extending the paginate function inside of app_controller.php and unsetting the options array order value if it is empty:
(line 1172 in cake/libs/controller/controller.php)
if(empty($options["order"])){
unset($options["order"]);
}
then the options will not be overwritten by thte blank array.
of course this should not be changed inside of controller.php, but put it in app_controller.php and move it to your app folder.
On CakePHP 3 this problem can be solved by adding 'sortWhitelist' params to $this->paginate on your controller.
$this->paginate = [
// ...
'sortWhitelist' => ['id', 'status', 'Attendance.Person.first_name']
];
And then in your view:
echo $this->Paginator->sort('Name', 'Attendance.Person.first_name');
This is noted in the docs:
This option is required when you want to sort on any associated data, or computed fields that may be part of your pagination query:
However that could be easily missed by tired eyes, so hope this helps someone out there!