CakePHP assocation question - cakephp

I'm creating a small timesheet application. Timesheets have athletes, and each athlete has personal split times (like in running, or race car driving)
An Athlete hasMany Run, a Run belongsTo Athlete, An Athlete hasAndBelongsToMany Timesheets (and vice versa). A Timesheet hasMany Run, and finally a Run belongsTo Timesheet.
When I'm adding new runs in my view, I'm unable to get anything but the athlete_id in the select box. I'd really like to have their names instead. Instead of
<?php echo $run['athlete_id'];?>, I've tried <?php echo $athlete['Athlete']['last_name'] ?> but I can't seem to get it to work. Any help would be greatly appreciated. Thanks!

Without knowing exactly how you are building your forms/data it is hard to tell, but how I would do it is.
In the RunController add
$athletes = $this->Run->Athlete->find('list');
$this->set('athletes', $athletes);
and then in the View use this form helper line.
<?php echo $form->input('Run.athlete_id', array('type' => 'select', 'options' => $athletes)); ?>
This should work, there is also a way to use 'compact' to make it a little easier but the above should work fine.
---- BEGIN EDIT ----
I did a little research and found the compact method.
In your RunController use
$athletes = $this->Run->Athlete->find('list');
$this->set(compact('athletes'));
and then in your View use
<?php echo $form->input('Run.athlete_id'); ?>
and the form helper will automatically find the compacted Athlete array and build the select.
---- END EDIT ----
Hope this helps.
Cheers,
Dean

Try printing out the content of the $run: print_r($run) and see if the ['Athlete'] is there.
If not, you might have to manually contain the Athlete model when you do your run query:
$this->Run->contain('Athlete');

Don't forget to use the displayField property of the Model class i.e.
<?php
class Athlete extends AppModel {
public $name = "Athlete";
public $displayField = "name"; // the field name that has the athletes name in it
}
?>
http://book.cakephp.org/view/438/displayField

Related

CakePHP 4.x hasMany foreach

I'm having some difficulty getting results from a hasMany relationship in my view file. I'm not getting any errors from the debug console so I'm not sure where I'm tripping up.
I think that maybe I'm not referencing correctly in the view file (I'm not really sure how to write a foreach function for this), if I'm not setting it up correctly in the table file, etc.
2 tables: "Clinicas", "Tratamientos" (services)
Goal: On a Clinic's profile page, I need to show the information for that Clinic (location, phone number, etc.) & list all of the services associated with that clinic.
ClinicasTable.php
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('clinicas');
$this->setDisplayField('name');
$this->setPrimaryKey('clinic_id');
$this->addBehavior('Timestamp');
$this->hasMany('Tratamientos', [
'foreignKey' => 'clinic_id',
'joinType' => 'INNER'
]);
}
ClinicasController.php
public function view($id = null)
{
$clinica = $this->Clinicas->get($id, [
'contain' => ['Tratamientos']
]);
$this->set(compact('clinica'));
}
view.php
<?php foreach($clinica as $tratamiento): ?>
<?= h($clinica->tratamiento->name)?>
<?php endforeach;?>
I've had a look at the documentation for associations but can't figure out how to get to data from my tables. I could always just do an ajax query through php functions, but I'd really like to do it right using CakePHP. Any help would be much appreciated!!!
Even though iterating is pretty much just basic PHP, the example in the book could be a little more helpful and show iterating over and using the associated data, even though it should already give you a good idea where you data lives.
You are obtaining a single entity (an instance of Clinica), so you cannot and should not iterate over it. What your code will do is iterate over all public properties of the entity object, which is not what you want, and won't do anything in your case, as by default entities do not have any concrete public properties.
The data of the association will be found on the association property of the Clinica entity, which, unless specifically configured otherwise via the association's property option, is the plural, underscored, lowercased variant of the association name, so for Tratamientos that would be tratamientos.
It should be noted that you really should stick to US english here for namings, as all the inflector magic around naming conventions is designed to work with that, names in other languages can easily cause mismatches/problems when they cannot be inflected properly!
Long story short, you iterate the $clinica object's tratamientos property, that's where the hasMany associated data lives:
<?php foreach($clinica->tratamientos as $tratamiento): ?>
<?= h($tratamiento->name)?>
<?php endforeach;?>
If you are unsure about a data structure, debug it: debug($clinica)

CakePHP 2.x Accessing Another Model

Noob to cakePHP here.
Bicycle belongsTo Stand
Stand hasMany Bicycle
Stand belongsTo Station
Station hasMany Stand
I've baked the CRUDs and now I want to display the station name when I view a bicycle. How should I define the bicycle - station relationship? Or what do I need to do to include the station model in the bicycles controller?
What I've tried so far. Which throws a "Notice (8): Undefined index: Station":
BicyclesController.php:
public function index() {
$this->Bicycle->recursive = 2;
$this->set('bicycles', $this->Paginator->paginate());
}
index.ctp:
<?php echo $this->Html->link($bicycle['Station']['name'].$bicycle['Stand']['stand_no'], array('controller' => 'stands', 'action' => 'view', $bicycle['Stand']['id'])); ?>
Help!
Since you have no direct relationship from Bicycle to Station, its not going to be available within $bicycle['Station'] Its more likely to be available in $bicycle['Stand']['Station]['name'].
In order to see if its actually being returned with recursive set as it is, I would run the query again and call $debug($bicycle);die; in the following order.
public function index() {
$this->Bicycle->recursive = 2;
$bicycles = $this->Paginator->paginate();
$debug($bicycle);die;
$this->set(compact('bicycles'));
}
This will kill the model at this point and display an array of results returned. You will then see where the information you want is, and how to access it. Just remove the $debug line when finished.

Look up data from a diffrent model using Cake

im new to cake (and loving it) but i have hit a problem that i cant seem to find a solution to. Im pretty sure there is an obvious answer to this so my apols in advance if i am asking a stupid question.
Okay, here goes.
I am trying to build a simple message system using Cake PHP version 2.
I have two models at the moment, one handles users (used for loggin in and out) and the other handles messages.
In the messages table i have the following columns:
id | sender_id | recipient_id | subject | body
My MessagesController.php reads as follows:
class MessagesController extends AppController {
public function recent_messages() {
if (empty($this->request->params['requested'])) {
throw new ForbiddenException();
}
return $this->Message->find(
'all'
);
}
}
My View is located in my elements as it actually displayed as a drop down in my navbar. Here is my view:
<?php
$messages = $this->requestAction('/messages/recent_messages');
?>
<?php
foreach ($messages as $message): ?>
<li>
<a href="#">
<div> <strong>Username of sender goes Here</strong></div>
<div><?php echo $message['Message']['subject']; ?></div>
</a>
</li>
<li class="divider"></li>
<?php endforeach; ?>
What i would like to do is to be able to get the actual username of the message sender using the sender_id. Just to clarify the sender_id value is actually the id (primary key) of the user in the users table.
Initially i thought this would be done through setting an association but i cant see how.
If anyone could offer any advice on this i would be most grateful.
I don't understand why you are using $this->requestAction
if your view is recent_messages.ctp you are already calling recentMessages().
Then if you set your relationships you are already retrieving all the data you need
$message['Sender']['username']
contains the information you are looking for. (or $message['User']['username'] it depends on how you wrote your model).
If not read the Manual about setting the relationships.
In brief all you have to do in your Message model is
$belongsTo = array(
'Sender' => array(
'className' => 'User',
'foreignKey' => 'sender_id'
)
)
One further thought.
It seems that you are doing the requestAction from the view. I think it can only be done from the controller and then you pass the result to the view via the $this->set('messages', $message) command.
If I've misinterpreted what you are doing then I'm sorry.

Cakephp FormHelper Create select box

i am cakephp beginner.
My Employee Model,
class Employee extends AppModel {
var $belongsTo = array(
'Department'=>array(
'className'=>'Department',
'foreignKey'=>'department_id',
'conditions'=>null,
'fields'=>null
)
);
blah--
now in employee add.ctp i want to create a select box which list all the department.
i was going through official cakephp 2.1 documentation (here)
it tells me to add
$this->set('departments', $this->Employee->Department->find('list'));
in my controller..
i have no idea to put in which controller ? is it in EmployeesController or DepartmentsController? and in which action of controller?
view to create select box (in add.ctp)
echo $this->Form->input('Department');
you were almost correct - only a minor glitch:
echo $this->Form->input('department_id');
you need to name the fields as they are in the database.
and if it is a BelongsTo relation than there should be a department_id foreign key in your employees table.
PS: cake knows that if you pass down $departments that this array will need to be the options for this form field. so no additional configuration necessary!
// in your add action at the very bottom
$departments = $this->Employee->Department->find('list');
$this->set(compact('departments'));

cakePHP hasOne relationship not auto completing dropdown field

I'm trying to implement a hasone relationship between 2 models, but I can't have the 'add' form autocomplete with the possible options in the second model (the one that belongsTo the first one). This is my code:
- model 1: item.php
<?php
class Item extends AppModel{
var $name = 'Item';
var $primaryKey = 'id';
var $hasOne = 'CoverImage';
}
?>
- model 2: cover_image.php
<?php
class CoverImage extends AppModel{
var $name = 'CoverImage';
var $primaryKey = 'id';
var $belongsTo = array(
'Item' => array(
'className' => 'Item',
'foreignKey' => 'item_id'
));
}
?>
- add view of model 2: add.ctp
<?php echo $this->Form->create('CoverImage',array('url' => array('controller' => 'admins', 'action' => 'add')));?>
<fieldset>
<legend><?php __('Info'); ?></legend>
<?php
echo $this->Form->input('item_id');
echo $this->Form->input('description');
?>
</fieldset>
<?php echo $this->Form->end(__('Create', true));?>
For what I see in Cake's documentation, with this relationship, in the add view I should see a dropdown list in the item_id field to be able to select to which item does this CoverImage belongs to, but the dropdown is empty (and yes, I have some items in the items table already).
Maybe I'm missing something or I've done something wrong, but I can't figure it out. Thanks so much in advance for any clues!
EDIT
One think I've just realized is that if I do this:
echo $this->Form->input('item_id', array('type'=>'text'));
instead of this:
echo $this->Form->input('item_id');
I can add/edit the *item_id* field, I can see its value in the text box. However, if I leave the other one, I just see an empty dropbox and when I try to add/edit a CoverImage, it doesn't work, it just shows an empty white page, not even with errors...
Maybe this is a lead to something...
In order for that to work you have to create a list of possible options in the controller. That does not happen automatically.
public function add() {
$items = $this->CoverImage->Item->find('list');
$this->set(compact('items'));
}
The FormHelper only automatically infers that the field item_id should be populated by the options in the variable $items (plural, no _id).
Do be careful that Items that already haveOne CoverImage should not be part of that list. find('list', array('conditions' => array('CoverItem.id' => null))) will probably* take care of that, but you'll need to recheck just before saving as well, or you need to rethink your associations.
* Not sure off the top of my head whether that'll work for 'list' searches.
EXCELLENT QUESTION. You've run afoul of a disingenuous feature of Cake's associations:
Considering you defined the relationship as hasOne? Guessing at the trace but Cake probably even correctly inferred your preference for list functionality. You got your automagic list...
...of One.
$hasOne is pretty exclusive like that. It "uses up" those "has" relationships (it's makes the relationship a de facto Singleton - so Users only have 1 Profile <-> Profile only has 1 User). Consider - Database can have many configurations, but Dbo will only ever have one Connection at a time and Connection will only have one Dbo. Thus -> hasOne is for marrying two Models til die() do they part.
-- So it doesn't get used nearly as much as hasMany and belongsTo.
For your purpose, you probably want to change to a different association.
Adding an additional $this->Item->find doesn't really fix what's wrong (and I wouldn't recommend it, unless you're mostly done with both models/controllers, or you actively want things to start getting weird fast.)
Also, changing how you call the Form Helper methods - if you return a 'list' type fetch from a find, Cake will automatically produce an option list out of it. What's actually happening is, you're sneaking around your Model on a very thin margin of View functionality. That's why specifying the input type to "break the magic" tends to be discouraged (which, you totally can if you want. Just understand what's actually happening, or: see, weird, fast.)
But you might want to rethink how you've associated your models - wouldn't it also be correct to say, each Item belongsTo a CoverImage (same as each CoverImage belongs to an Item) -- because you have a form expressly permitting a CoverImage to select an Item, any Item, to be displayed with? You'll probably get better results.
HTH. :)

Resources